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

Java多线程wait()阻塞与唤醒逻辑实现正确性咨询

Java wait/notify 并发代码正确性校验

初始等待写法校验

最初的等待代码片段:

owner=Thread.currentThread();
owner.wait();

结论:写法完全错误,核心问题是调用wait()方法前没有持有owner对象的监视器锁,运行时会直接抛出IllegalMonitorStateException。

第一版完整实现问题

用户最初编写的完整等待逻辑:

private Thread owner;
public void join() {
    Thread thread= new Thread(()-> {
        try {
            owner= Thread.currentThread();
            owner.wait();               
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    thread.start();
}

配套唤醒逻辑:

public void wakeUp() {          
        notifyAll();
}

该版本存在以下致命问题:

  • 违反wait/notify基础使用规则:所有wait/notify/notifyAll调用必须在持有对应对象监视器锁的同步块/同步方法中执行,上述代码两处调用都没有加同步保护,必然抛出非法监视器状态异常。
  • 锁对象不匹配:等待操作是调用owner实例的wait(),唤醒操作是调用当前实例this的notifyAll(),二者作用在完全不同的对象上,即使补全同步块也无法唤醒等待线程。
  • 跨线程可见性无保障:owner变量没有同步保护也没有volatile修饰,子线程对owner的赋值对执行wakeUp的线程不一定可见,极端场景下会触发空指针异常。
  • 方法命名歧义:自定义方法命名为join(),和Thread类原生的线程等待方法重名,容易造成语义混淆。

累计5次等待后唤醒的更新版代码校验

更新版目标是实现累计5次wait()调用后统一唤醒所有等待线程,代码如下:

private Object mutex = new Object();
private volatile int counter;
private boolean condition=false;

public void enter() {
    synchronized (mutex) {
        counter++;
        while (!condition) {
            try {
                mutex.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }   
        exit();
    }
}
public void exit() {
    System.out.println("exit()");
 }

public void checkK() {
    synchronized (mutex) {
        if(counter>=5) {
            mutex.notifyAll();
            counter=0;
            condition=true;
        }
    }   
}

符合规范的部分

  • 专门定义了私有专用锁对象mutex,没有使用Thread、String等可能被外部代码复用的公共对象作为锁,避免意外锁竞争。
  • 所有wait/notify操作都在synchronized (mutex)同步块内执行,持有正确的监视器锁,不会抛出非法监视器状态异常。
  • wait条件判断使用while循环而非if判断,符合Java官方推荐的wait编程范式,可以规避虚假唤醒导致的逻辑错误。

存在的缺陷

  • 核心逻辑不可复用:第一次凑够5个等待线程触发唤醒后,condition变量永远为true,后续所有调用enter()的线程都不会进入等待,会直接执行exit(),无法重复执行「累计5次等待再唤醒」的逻辑,需要在唤醒后合适的时机将condition重置为false。
  • 唤醒触发无保障:checkK()方法没有和enter()做联动触发,如果累计等待线程数始终达不到5,且没有外部代码主动调用checkK(),所有等待线程会永久阻塞。
  • 中断处理不规范:捕获InterruptedException后仅打印栈轨迹,没有恢复线程中断标记,会丢失中断信号,不符合并发编程最佳实践,正确做法是捕获异常后调用Thread.currentThread().interrupt()恢复中断状态,或向上抛出异常交由上层处理。
  • 多余修饰:counter变量的所有读写操作都在synchronized同步块保护范围内,synchronized本身已经保证了变量的可见性和原子性,额外加volatile修饰属于冗余代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 10:15:32