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(); } }
关键修改点说明
新增执行条件的循环检查:
- t1通过
n % 2 != 0判断是否需要等待,只有当n是偶数时才执行递增; - t2通过
n % 2 == 0判断是否需要等待,只有当n是奇数时才执行递增; - 这样即使初始信号丢失,线程也会先检查条件,满足就直接执行,不会盲目await。
- t1通过
用while包裹await处理虚假唤醒:
- 把
await()放在while循环里,确保线程被唤醒后会重新检查条件,避免虚假唤醒导致的逻辑错误。
- 把
调整lock和循环的结构:
- 将
while(n < LIMIT)放到lock保护的范围内,减少不必要的lock获取操作; - 把lock、Condition的定义提到方法外,避免重复创建(原代码中每次调用
runWithReentrantLock都会创建新的lock,这里是优化)。
- 将
优化异常处理:
- 捕获
InterruptedException后,调用Thread.currentThread().interrupt()恢复中断状态,符合并发编程的最佳实践。
- 捕获
这样修改后,两个线程就能稳定地交替递增n,直到达到LIMIT值,不会再出现死等待的问题啦!
内容的提问来源于stack exchange,提问作者John Shen
相关产品推荐
相关产品推荐

