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
相关产品推荐
相关产品推荐

