Java多线程同步问题:仅用wait/notify实现线程互斥运行
用wait()/notify()实现线程互斥执行的解决方案
看起来你在尝试用wait/notify实现线程间的互斥执行,核心问题出在锁的共享性和条件控制的逻辑上。我来帮你拆解问题,然后给出可运行的修正方案。
你的代码存在的关键问题
- 锁不共享:把
synchronized加在run()方法上是无效的——每个线程对象的run()方法锁的是线程实例本身,多个线程的锁完全独立,根本无法阻止其他线程同时执行。 - 控制变量不共享:如果
lock是每个线程自己的成员变量,那它无法全局控制所有线程的执行状态,必须是所有线程共用的变量。 - 未正确唤醒线程:任务执行完后没有重置控制标记,也没有调用
notify()/notifyAll(),导致其他线程会一直卡在wait()中无法唤醒。
正确的实现方案
我们需要一个共享的锁对象让所有线程同步,再用一个共享的状态变量标记当前允许运行的线程类型,每个线程在执行前先检查状态,不符合就等待;执行完任务后切换状态并唤醒其他等待的线程。
完整代码示例
// 共享控制类:管理线程的执行状态和锁 class CarControl { // 标记当前允许运行的方向(Left/Right) private String currentDirection; // 全局共享锁对象,所有线程共用这一个锁 private final Object lock = new Object(); public CarControl(String initialDirection) { this.currentDirection = initialDirection; } // 左侧车辆执行逻辑 public void runLeftCar(String leftMessage) throws InterruptedException { synchronized (lock) { // 用while循环判断条件,防止虚假唤醒 while (!"Left".equals(currentDirection)) { lock.wait(); } // 执行左侧任务 System.out.println("LeftSideCar: " + leftMessage); System.out.println("State: Left"); // 这里可以调用你的Direction()方法 // Direction(); // 任务完成后切换方向,唤醒所有等待的线程 currentDirection = "Right"; lock.notifyAll(); } } // 右侧车辆执行逻辑 public void runRightCar(String rightMessage) throws InterruptedException { synchronized (lock) { while (!"Right".equals(currentDirection)) { lock.wait(); } // 执行右侧任务 System.out.println("RightSideCar: " + rightMessage); System.out.println("State: Right"); // Direction(); // 切换方向并唤醒所有线程 currentDirection = "Left"; lock.notifyAll(); } } } // 左侧线程类 class LeftCarThread extends Thread { private final CarControl control; private final String message; public LeftCarThread(CarControl control, String message) { this.control = control; this.message = message; } @Override public void run() { try { // 模拟多次执行任务,可根据需求调整 for (int i = 0; i < 3; i++) { control.runLeftCar(message + " - 第" + (i+1) + "次"); // 模拟任务间隔,可选 Thread.sleep(500); } } catch (InterruptedException e) { // 正确处理中断:恢复中断状态 Thread.currentThread().interrupt(); System.out.println("左侧车辆线程被中断"); } } } // 右侧线程类 class RightCarThread extends Thread { private final CarControl control; private final String message; public RightCarThread(CarControl control, String message) { this.control = control; this.message = message; } @Override public void run() { try { for (int i = 0; i < 3; i++) { control.runRightCar(message + " - 第" + (i+1) + "次"); Thread.sleep(500); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println("右侧车辆线程被中断"); } } } // 测试主类 public class LeftRightCarTest { public static void main(String[] args) { // 初始允许左侧车辆先运行 CarControl control = new CarControl("Left"); LeftCarThread leftThread = new LeftCarThread(control, "左转通行"); RightCarThread rightThread = new RightCarThread(control, "右转通行"); leftThread.start(); rightThread.start(); } }
核心要点解释
- 共享锁对象:所有线程都使用
CarControl中的lock对象进行同步,确保同一时间只有一个线程进入同步块,实现互斥。 - while循环判断条件:必须用
while而非if检查状态,因为wait()可能被虚假唤醒(其他线程调用notifyAll()但当前线程的执行条件仍不满足),每次唤醒后都要重新验证条件。 - 任务完成后唤醒线程:执行完自身任务后,更新状态并调用
notifyAll(),唤醒所有等待的线程,让符合条件的线程继续执行。 - 正确处理中断:当线程被中断时,恢复中断状态并做必要的清理,避免线程状态异常。
注意事项
- 调用
wait()、notify()、notifyAll()必须在synchronized块/方法内,且针对同一个锁对象。 - 永远不要在
if语句中调用wait(),虚假唤醒是线程编程中常见的场景,必须用while循环处理。 - 共享变量(比如
currentDirection)不需要额外加volatile,因为它的读写都在synchronized块内,已经保证了可见性。
内容的提问来源于stack exchange,提问作者molamola
相关产品推荐
相关产品推荐

