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

为何以下Java代码会引发不稳定死锁?synchronized验证存疑

问题分析与解决

线程阻塞(类似死锁)的核心原因

你的代码里没有传统意义上“双向持锁等待”的死锁,但出现的线程阻塞本质是可见性缺失导致无限循环+锁无法释放,具体逻辑如下:

  1. 可见性问题引发无限循环
    atomicInteger 未被 volatile 修饰,根据Java内存模型,writer 和 reader 线程对它的修改(执行dd()里的atomicInteger++)无法保证被执行test()方法的线程感知。也就是说,哪怕atomicInteger已经被改成2,test()里的死循环还是会一直读取自己工作内存里的旧值0,永远不会触发lock.unlock()逻辑。

  2. 锁无法释放导致后续线程排队阻塞
    每个test()线程启动后先调用lock.lock()获取了公平锁ReentrantLock,但因为无限循环,锁永远不会被释放。volatileTest()里启动了100个这样的test()线程,后续线程会一直排队等待获取这把锁,最终造成大量线程阻塞,表现得像死锁。

额外问题点

  • dd()用synchronized修饰,已经保证了atomicInteger++的原子性,但代码里的ReentrantLock和synchronized是完全独立的两把锁,没有关联,属于冗余设计。
  • test()里的忙等死循环会持续占用CPU资源,效率极低。

修复方案

  1. 给atomicInteger添加volatile修饰,保证多线程间的变量可见性:
    private volatile int atomicInteger = 0;
    
  2. 替换忙等逻辑,用更高效的线程同步方式(比如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块,保证锁一定会被释放
        }
    }
    
  3. 锁逻辑优化:如果只是验证synchronized的功能,可以直接去掉ReentrantLock,避免不必要的锁竞争。

内容的提问来源于stack exchange,提问作者沃司机i

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 08:12:37