在notify()之后执行大量工作将导致wait()变成busy wait



如果我有下面的代码

synchronized (this)
{
    System.out.println("Waiting for return key.");
    scanner.nextLine();
    System.out.println("Return key pressed.");
    notify();
    Thread.sleep(5000);
}

notify之后,我调用sleep,这意味着,我已经通知了等待线程,但没有放弃锁,现在发生了什么…通知后,等待线程将被唤醒,但无法获得锁,因此从这里开始,它是一个繁忙的等待吗?因为我们不打算再次调用notify

同样的问题也适用于notifynotifyall,在一个线程唤醒并获得锁后,是否所有其他线程等待变得繁忙等待?

wait()不会忙等待,但一旦收到通知,它会"以通常的方式与其他线程竞争同步对象的权利"。

notify的调用唤醒当前正在对象条件队列上等待的一个线程,然后该线程尝试获取此时仍由调用线程持有的锁。因此,这种情况类似于一个线程想要进入当前由另一个线程执行的synchronized块。线程没有执行忙等待,它只是被阻塞,直到它可以获得锁。

当调用notify的线程释放锁时,另一个线程可以被解除阻塞并继续工作。

对于notifyAll也是如此,但是它唤醒所有在对象条件队列上等待的线程。因为只有一个线程可以获得锁,所以其他线程一直处于阻塞状态,直到它们一个接一个地获得锁。由于线程唤醒信号可能会自发发生,因此需要在条件循环中始终调用wait:

synchronized (lockObject) {
    // ...
    while (!condition) {
        lockObject.wait();
    }
    // object is now in desired state
}

参见:Java并发实践,第14.2章

线程只有在拥有对象的监视器时才能等待对象。一旦第一个线程发出通知,第二个线程就会被唤醒,但不做任何事情。这里发生的唯一事情是"线程将从等待对象的线程列表中删除"。它的执行是留给操作系统来安排的。操作系统可能会选择暂时不执行它。线程不会忙等待。它只是在等待调度的线程集中。

正如@Holger指出的,任何调用wait()的线程都会释放对象上的锁。一旦收到通知,它就必须"竞争"并重新获取对象上的锁。当持有锁的线程调用notify()时,不会重新获取锁。当线程退出它的同步块时发生。

最新更新