为何以下Java代码会引发不稳定死锁?synchronized验证存疑
问题分析与解决
线程阻塞(类似死锁)的核心原因
你的代码里没有传统意义上“双向持锁等待”的死锁,但出现的线程阻塞本质是可见性缺失导致无限循环+锁无法释放,具体逻辑如下:
可见性问题引发无限循环
atomicInteger未被volatile修饰,根据Java内存模型,writer和reader线程对它的修改(执行dd()里的atomicInteger++)无法保证被执行test()方法的线程感知。也就是说,哪怕atomicInteger已经被改成2,test()里的死循环还是会一直读取自己工作内存里的旧值0,永远不会触发lock.unlock()逻辑。锁无法释放导致后续线程排队阻塞
每个test()线程启动后先调用lock.lock()获取了公平锁ReentrantLock,但因为无限循环,锁永远不会被释放。volatileTest()里启动了100个这样的test()线程,后续线程会一直排队等待获取这把锁,最终造成大量线程阻塞,表现得像死锁。
额外问题点
dd()用synchronized修饰,已经保证了atomicInteger++的原子性,但代码里的ReentrantLock和synchronized是完全独立的两把锁,没有关联,属于冗余设计。test()里的忙等死循环会持续占用CPU资源,效率极低。
修复方案
- 给
atomicInteger添加volatile修饰,保证多线程间的变量可见性:private volatile int atomicInteger = 0; - 替换忙等逻辑,用更高效的线程同步方式(比如
CountDownLatch),避免CPU空转:
修改后的test()方法示例:public void test() { lock.lock(); try { atomicInteger = 0; CountDownLatch latch = new CountDownLatch(2); Thread writer = new Thread(() -> { dd(); latch.countDown(); }); Thread reader = new Thread(() -> { dd(); latch.countDown(); }); writer.start(); reader.start(); // 等待两个线程执行完成,无需空循环 latch.await(); System.out.println(Thread.currentThread().getName()+"Lock released!"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { lock.unlock(); // 放在finally块,保证锁一定会被释放 } } - 锁逻辑优化:如果只是验证
synchronized的功能,可以直接去掉ReentrantLock,避免不必要的锁竞争。
内容的提问来源于stack exchange,提问作者沃司机i
相关产品推荐
相关产品推荐

