You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java多线程同步块死循环问题:并发数限制失效排查

线程计数控制逻辑死循环问题排查

咱们来一步步揪出你代码里的问题,导致Thread-5陷入死循环、count始终卡在5的根源其实是两个关键错误,尤其是锁的使用逻辑出了大问题:

1. 同步方法内调用Thread.sleep()会持续持有锁,阻塞所有后续操作

你的incrementCounter是static synchronized方法,这意味着只要有线程进入这个方法,就会独占MiscUtils类的锁。当第6个线程进来时,发现count=5,进入while循环执行Thread.sleep(5000)——但这里的sleep不会释放锁!

而你的decrementCounter同样是static synchronized方法,必须获取同一把锁才能执行。这就形成了死锁链:

  • 前5个线程执行完incrementCounter后,会sleep 10秒,但还没来得及调用decrementCounter;
  • 第6个线程拿着锁在sleep,前5个线程醒后想调用decrementCounter却拿不到锁,无法减少count;
  • 第6个线程每次sleep醒来,检查count还是5,继续sleep,彻底陷入死循环。

2. 用sleep做等待逻辑本身就是错误的选择

Thread.sleep()是让线程休眠固定时间,不会响应count的实时变化。哪怕后续count被修改了,休眠的线程也要等够时间才会检查,而且如上面所说,它还拿着锁不让别人修改count,完全违背了“等待可用名额”的设计初衷。


修正方案:用wait/notify替代sleep,正确释放锁

解决这个问题的核心是让等待的线程释放锁,允许其他线程修改count,同时在count变化时及时唤醒等待的线程。下面是修正后的完整代码:

import java.util.Date;

public class MiscUtils {
    private static int count = 0;
    // 用单独的锁对象,比类锁更灵活,避免锁范围过大
    private static final Object lock = new Object();

    public static int getCount() {
        synchronized (lock) {
            return count;
        }
    }

    public static boolean incrementCounter() throws InterruptedException {
        synchronized (lock) {
            // 等待直到有可用名额(count < 5)
            while (count >= 5) {
                System.out.println(Thread.currentThread().getName() + " Waiting..");
                lock.wait(); // wait会释放锁,让其他线程可以修改count
            }
            System.out.println("Thread acquired " + Thread.currentThread().getName());
            count++;
            return true;
        }
    }

    public static boolean decrementCounter() {
        synchronized (lock) {
            System.out.println("Decrement Called " + Thread.currentThread().getName());
            count--;
            lock.notify(); // 通知等待的线程:count已经减少,有可用名额了
            return true;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    MiscUtils.incrementCounter();
                    Thread.sleep(10 * 1000); // 业务逻辑的sleep不需要持有锁,没问题
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 用finally确保decrement一定会被调用,避免计数泄漏
                    MiscUtils.decrementCounter();
                }
                System.out.println(Thread.currentThread().getName() + " finished");
            }
        };
        int threadNum = 10;
        for (int i = 0; i < threadNum; i++) {
            Thread s = new Thread(r);
            System.out.println(new Date() + " Thread " + i + " starting..");
            s.start();
            System.out.println(new Date() + " Thread " + i + " started..");
            Thread.sleep(1 * 1000);
        }
    }
}

关键修正点说明:

  • 用lock.wait()替代Thread.sleep():wait会主动释放锁,让其他线程可以进入同步块修改count;
  • 用lock.notify()触发唤醒:每次decrement后通知等待的线程,让它们重新检查count是否有可用名额;
  • 将decrement放入finally块:确保无论业务逻辑是否抛出异常,count都会被正确减少,避免计数永久卡在某个值;
  • 使用独立锁对象:比类锁更灵活,也能避免其他无关的同步操作影响这个计数逻辑。

内容的提问来源于stack exchange,提问作者SaratBhaswanth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:10:04