obj.wait() 方法注意事项

wait() 方法使用示例

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait(timeout);
     ... // Perform action appropriate to condition
 }

问: 这里的条件判断为什么要使用 while 循环,而不直接使用 if?

原因一: java api 文档推荐

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops.

除了被唤醒,中断,或达到超时时间之外,一个线程仍然可以唤醒,所谓的虚假唤醒,虽然(这种情况)实际发生的概率很低,但是应用程序还是应该通过检测条件是否满足(不满足时继续等待)来处理这种情况.换句话说,wait() 方法应该总是写在循环里面.

原因二: 梅莎管程模型

image
这里画了 a.q 和 b.q 两个条件变量等待队列,而 synchronized 使用的是简化的梅莎管程模型,只支持一个条件变量等待队列.

线程被唤醒之后不会立即执行,而是进入入口就绪队列,当这个被唤醒的线程再次获取 CPU 执行权后,会从调用 wait() 方法之后的地方开始执行,但是此时条件可能仍然不满足,因此必须重新检查,也因此 wait() 方法应该写在循环里面.

测试
public static void main(String[] args) throws InterruptedException {

    Object obj = new Object();

    boolean condition = false;

    Thread thread = new Thread(() -> {

        synchronized (obj) {
            while (!condition) {
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 若使用 if,则不管 condition 是否满足,thread 线程被唤醒后都会打印 hello
            System.err.println("hello");
        }
    });

    thread.start();

    Thread.sleep(1000L);

    synchronized (obj) {
        obj.notifyAll();
    }

}
posted @ 2021-07-06 16:23  凛冬雪夜  阅读(150)  评论(0)    收藏  举报