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

Java双线程交替递增整数异常求助:线程常陷入等待

两个线程交替递增整数时死等待问题的分析与修复

嘿,作为Java并发编程的新手,你能想到用ReentrantLock和Condition来实现线程交替已经很棒了!不过你的代码里有两个关键问题,导致线程经常陷入无法唤醒的死等待状态,咱们一起来解决它:

问题根源拆解

1. 初始信号丢失,导致线程永久等待

主线程在启动t1和t2后,立刻发送t1CanRun.signal()唤醒t1,但此时t1可能还没走到t1CanRun.await()这一步——线程启动需要时间,可能主线程的signal已经发送完毕,t1才刚进入lock并执行await。这时候这个初始信号就“浪费”了,t1会一直卡在await,而t2也卡在t2CanRun.await()等待t1的signal,最终两个线程都挂起。

2. 未处理虚假唤醒,且缺少执行条件的循环检查

Java的Condition.await()存在虚假唤醒的可能(线程会在没有被signal的情况下醒来),而且你的代码没有在await前检查是否真的轮到当前线程执行。即使没有初始信号丢失,也可能因为虚假唤醒或者条件变化导致线程执行逻辑混乱,甚至再次进入不必要的await。

修复后的代码

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class TwoThreads {
    private final int LIMIT = 500;
    private int n = 0; // 被lock保护,无需volatile
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition t1CanRun = lock.newCondition();
    private final Condition t2CanRun = lock.newCondition();

    public void runWithReentrantLock() {
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                while (n < LIMIT) {
                    // 循环检查:只有当n是偶数时,t1才执行(初始n=0,t1先启动)
                    while (n % 2 != 0) {
                        t1CanRun.await();
                    }
                    if (n < LIMIT) {
                        n++;
                        System.out.println(Thread.currentThread().getName() + "-->" + n);
                    }
                    // 通知t2可以执行
                    t2CanRun.signal();
                }
            } catch (InterruptedException e) {
                // 恢复中断状态,避免中断信息丢失
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            lock.lock();
            try {
                while (n < LIMIT) {
                    // 循环检查:只有当n是奇数时,t2才执行
                    while (n % 2 == 0) {
                        t2CanRun.await();
                    }
                    if (n < LIMIT) {
                        n++;
                        System.out.println(Thread.currentThread().getName() + "-->" + n);
                    }
                    // 通知t1可以执行
                    t1CanRun.signal();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }, "t2");

        t1.start();
        t2.start();
    }

    public static void main(String[] args) {
        TwoThreads instance = new TwoThreads();
        instance.runWithReentrantLock();
    }
}

关键修改点说明

  1. 新增执行条件的循环检查:

    • t1通过n % 2 != 0判断是否需要等待,只有当n是偶数时才执行递增;
    • t2通过n % 2 == 0判断是否需要等待,只有当n是奇数时才执行递增;
    • 这样即使初始信号丢失,线程也会先检查条件,满足就直接执行,不会盲目await。
  2. 用while包裹await处理虚假唤醒:

    • 把await()放在while循环里,确保线程被唤醒后会重新检查条件,避免虚假唤醒导致的逻辑错误。
  3. 调整lock和循环的结构:

    • 将while(n < LIMIT)放到lock保护的范围内,减少不必要的lock获取操作;
    • 把lock、Condition的定义提到方法外,避免重复创建(原代码中每次调用runWithReentrantLock都会创建新的lock,这里是优化)。
  4. 优化异常处理:

    • 捕获InterruptedException后,调用Thread.currentThread().interrupt()恢复中断状态,符合并发编程的最佳实践。

这样修改后,两个线程就能稳定地交替递增n,直到达到LIMIT值,不会再出现死等待的问题啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:50:14