Java 为什么线程间通信需要notify()

Java 为什么线程间通信需要notify(),java,multithreading,Java,Multithreading,从对象的JAVA文档notify() 唤醒的线程将无法继续,直到当前 线程放弃此对象上的锁定 这意味着,除非发出通知的线程完成了同步块并释放了锁,否则等待的线程将无法继续。如果是这种情况,那么如果同步块仍要执行,那么让notify()有什么意义呢?如果不唤醒等待的线程并让它完成它的工作,notify()的实际用途是什么?严格来说,我们不会:我们可以让等待的线程运行一个循环,重新获取锁,检查条件,并在短时间内休眠。但是使用wait()和notify()效率更高,因为这样等待的线程就不会一直唤醒并占

从对象的JAVA文档
notify()

唤醒的线程将无法继续,直到当前 线程放弃此对象上的锁定


这意味着,除非发出通知的线程完成了同步块并释放了锁,否则等待的线程将无法继续。如果是这种情况,那么如果同步块仍要执行,那么让
notify()
有什么意义呢?如果不唤醒等待的线程并让它完成它的工作,
notify()
的实际用途是什么?

严格来说,我们不会:我们可以让等待的线程运行一个循环,重新获取锁,检查条件,并在短时间内休眠。但是使用
wait()
notify()
效率更高,因为这样等待的线程就不会一直唤醒并占用CPU(并占用锁)。

好问题。我会指给你看一下

调用
对象.notify
方法的线程启用以前调用
对象的线程。现在,线程调度程序将启用wait
。换句话说,等待的线程现在是“
runnable
”。虽然它是“可运行的”,但它不是“运行的”

它只能在调用
notify
的线程释放锁时继续运行-一种方法是当它退出同步块时


网上有很多关于线程状态的示意图。其中一些完全不正确或令人困惑,因为它们引入了官方文件中没有的术语。这对我来说很有意义。

想象一下,如果您需要一个线程等待另一个线程执行它当前可能正在或可能尚未积极处理的任务。例如,一个正在等待作业执行的线程可能需要等待,直到另一个线程将一个作业放在它应该执行的作业列表中(如果该列表为空)。你会怎么做

你不能仅仅使用某种形式的互斥。可能有很长一段时间没有工作要做,并且线程没有在队列上持有任何锁。现在可能没有什么工作要做。执行工作的线程需要等待,而不持有任何锁,直到另一个线程给它一些工作要做

所以在某个地方,有一个线程可以做这样的事情:

  • 获取保护另一个线程可能正在等待更改的某些共享状态的锁。(在本例中,是作业队列。)
  • 更改共享状态以反映线程可能需要等待的事情已经发生的事实。(即,将作业放入队列。)
  • 释放锁并让所有等待的线程知道事情已经发生
  • 那么我们等待的代码会是什么样子呢?也许:

  • 获取保护共享状态的锁
  • 检查我们是否需要等待。(队列中有作业吗?)
  • 如果我们需要等待,请等待。(如果没有,请等待作业放置在队列上。)
  • 哎呀,我们有个问题。我们等待的事情不会发生,因为我们握着锁。没有其他线程可以更改共享状态。(在释放步骤1中获得的锁之前,我们将作业放入队列的线程无法触及队列。)

    让我们再试一次:

  • 获取保护共享状态的锁
  • 检查我们是否需要等待。(队列中有作业吗?)
  • 如果不需要等待,请退出此算法。(如果有作业,请将其从队列中取出,释放锁,然后执行。)
  • 松开锁。(因此,另一个线程可以将作业放入队列。)
  • 等待事情发生。
  • 哎呀,我们还有一个问题。如果我们等待的事情发生在第4步之后但在第5步之前呢。既然锁被释放了,我们等待的事情就会发生。我们不能再检查了,因为我们没有锁。我们如何确保我们不等待已经发生的事情,这可能意味着永远等待


    为了解决这个问题,我们需要一个原子“解锁并等待”操作。这就是等待所做的。我们还需要一些操作来结束这个等待,它可以被改变共享状态的线程调用,这样我们就不需要再等待了。这就是notify所做的。

    Notifying就是唤醒正在等待的线程。如果删除了notify,那么等待线程将保持等待状态(除非出现虚假唤醒,但我们暂时不要去那里)

    (中断会唤醒线程,但指导是仅将其用于取消。中断以特定线程为目标,通知让计划程序决定哪些线程受到影响。)

    当线程调用wait时,它必须拥有锁,然后wait方法释放锁

    当线程调用notify时,它必须拥有锁

    实际上,在通知线程放弃锁之前,通知不能在任何等待的线程上生效。无论如何,通知线程需要做的第一件事就是尝试获取锁。您引用的所有段落都是想说,当线程调用notify时,唤醒不会立即发生

    因此,这里发生的情况是,通知线程释放锁并将通知发送给调度程序,调度程序决定通知哪个线程,然后通知线程醒来并争夺锁,以便离开wait方法

    notify()
    notifyAll()
    用于唤醒在调用
    notify()
    notifyAll()
    的同一对象上调用
    wait()
    的线程。 如果不调用
    notify()
    ,那些“等待”的线程将永远等待(尽管JVM规范说线程有时可能会在不调用notify的情况下醒来)。 另外,因为调用
    notify()
    不会释放关联锁
    synchronized(lockObject) {
        if (size < LIMIT) {
            addElement();
            lockObject.notifyAll(); //notifying threads that are waiting to get element from empty queue
        } else {
            lockObject.wait(); // waiting for other thread to get element from queue and make room for new element
        }
    }
    
    synchronized(lockObject) {
        if (size > 0) {
            getElement();
            lockObject.notifyAll(); // notify threads that there is a room for new element
        } else {
            lockObject.wait(); // waiting for other thread to put element into the queue
        }
    }