多线程学习问题:unlock方法中notify失效,等待线程无法唤醒
问题分析与修复方案
核心问题拆解
- isOpen变量逻辑完全错误:这个static标记的设计逻辑混乱,隧道满时设为true,解锁时直接置为false,会导致等待线程无法正确判断当前隧道状态,甚至出现多个线程同时错误进入的情况。
- AtomicInteger纯属冗余:既然已经用
synchronized (this)保证了锁内操作的原子性,再用AtomicInteger完全没必要,反而增加了代码复杂度。 - wait条件判断错误:等待的触发条件应该是隧道内车辆数达到上限,而非依赖全局isOpen标记;而且wait必须放在while循环中(这是多线程编程的硬性规范,防止虚假唤醒)。
- 通知逻辑不严谨:notify()只会唤醒一个等待线程,但当隧道有空位时,可能有多个线程在等,只用notify可能导致部分线程永远无法被唤醒。
修复后的代码
public class Tunnel { // 用普通int即可,synchronized已保证原子性 private int currentCars = 0; // 把最大容量抽成常量,便于维护 private static final int MAX_TUNNEL_CARS = 3; void goIntoTunnel() throws InterruptedException { acquirePermission(); try { int stayTime = (int) (Math.random() * 5000); System.out.println(Thread.currentThread().getName() + " 进入隧道"); Thread.sleep(stayTime); } finally { // 必须用finally确保释放权限,避免异常导致死锁 releasePermission(); } System.out.println(Thread.currentThread().getName() + " 离开隧道,停留时间: " + stayTime); } void acquirePermission() throws InterruptedException { synchronized (this) { // 循环检查:只要隧道满就等待 while (currentCars >= MAX_TUNNEL_CARS) { wait(); } // 条件满足,进入隧道,计数加1 currentCars++; } } void releasePermission() { synchronized (this) { // 离开隧道,计数减1 currentCars--; // 唤醒所有等待线程,让它们重新检查准入条件 notifyAll(); } } }
关键修复说明
- 移除冗余的AtomicInteger:在synchronized块内,普通int的读写操作是原子的,完全可以替代AtomicInteger,代码更简洁。
- 修正等待触发逻辑:直接用
currentCars >= MAX_TUNNEL_CARS作为wait的判断条件,放在while循环中,确保线程被唤醒后重新检查状态,彻底避免虚假唤醒的问题。 - 替换notify为notifyAll:当有线程离开隧道时,唤醒所有等待线程,让它们竞争进入隧道(只用notify可能导致部分线程一直处于等待状态)。
- 用finally保证解锁:在进入隧道后的业务逻辑外层加finally块,确保无论是否抛出异常,都能释放隧道权限,避免死锁。
- 移除错误的isOpen变量:直接用当前隧道内车辆数作为准入判断的唯一依据,逻辑清晰且可靠。
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

