为什么 'notify' 唤醒了所有等待的线程,尽管只有一个线程应该受到影响?

Why does 'notify' wake up all waiting threads although only one thread should be affected?

在下面的代码中,两个消费者线程启动并等待。生产者线程在那之后开始(很可能)并调用 'notify'。所有线程都使用生产者作为监视器。

Thread producer = new Thread() { 
    @Override
    public void run() {
        synchronized (this) {
            System.out.printf("notify at %d %n", getId());
            notify();
        }
    }
};

Runnable consumer = () -> {
    try {
        synchronized (producer) {
            long id = Thread.currentThread().getId();
            System.out.printf("wait at %d %n", id);
            producer.wait();
            System.out.printf("awakened: %d %n", id);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};

Stream.generate( () -> consumer )
        .limit(2)
        .map(Thread::new)
        .forEach(Thread::start);

Thread.sleep(3000); // consumer threads are (likely) waiting

producer.start();

来自 Object.notify 的 javadoc:

Wakes up a single thread that is waiting on this object's monitor.

代码产生这个(或类似的)输出:

  wait at 13 
  wait at 14 
  notify at 12 
  awakened: 13 
  awakened: 14 

重点是两个消费者线程都被唤醒,而不是只有一个。为什么?

在 Windows 10、64 位下使用 OpenJDK 运行时环境 AdoptOpenJDK(内部版本 11.0.5+10)编译和测试。

提前致谢!

问题是您使用 Thread 作为监视器而不是任意 Object

Thread 在内部使用信号,如 Thread.join:

中所述

As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

一般建议始终使用专用对象 wait/notify,其他代码无法访问该对象以避免 "spurious" 像这样通知或等待。