Java JVM是否保证正确的;“等等”;将在呼叫“时收到通知”;通知;?

Java JVM是否保证正确的;“等等”;将在呼叫“时收到通知”;通知;?,java,multithreading,Java,Multithreading,据一位同事说,JVM不能保证在对对象调用“notify”时,会在那个时候通知正确的“wait”。他说,可能会有这样一种情况:以前的通知在无效时间发出,但现在已经无效了 这是真的吗?如果是这样的话,这是怎么/为什么的?如果你不能假设这样的基本机制可以工作,那么等待/通知机制有什么用?对于java.lang.Object.notify,Javadoc说: 唤醒正在该对象监视器上等待的单个线程。如果 任何线程都在等待此对象,其中一个线程被选中 觉醒了。该选择是任意的,由客户自行决定 实施线程通过调用对

据一位同事说,JVM不能保证在对对象调用“notify”时,会在那个时候通知正确的“wait”。他说,可能会有这样一种情况:以前的通知在无效时间发出,但现在已经无效了


这是真的吗?如果是这样的话,这是怎么/为什么的?如果你不能假设这样的基本机制可以工作,那么等待/通知机制有什么用?

对于java.lang.Object.notify,Javadoc说:

唤醒正在该对象监视器上等待的单个线程。如果 任何线程都在等待此对象,其中一个线程被选中 觉醒了。该选择是任意的,由客户自行决定 实施线程通过调用对象的监视器来等待对象的监视器 等待的方法

以下是等待特定条件的模式:

synchronized( lock ) {
   while( conditionEvaluation( data )) {
      lock.wait();
   }
}

对应方应该使用
java.lang.Object.notifyAll()
来确保应用程序的活力。即使在今天,它只是一个服务生,但经过软件的多次进化后,将来可能会有几个服务生,因此
notifyAll()
notify()

每个等待内在锁的对象都会进入锁的等待集。在锁定对象上调用notify时,将选择其等待集中的一个线程来恢复工作。JVM提供的唯一保证是等待的线程最终会得到通知。这种非确定性行为的主要原因之一是JVM选择挂起线程运行的方式,这是任意的。然而,除此之外,java中的锁实现了一种非公平的锁定策略,该策略允许线程交换。这仅仅意味着允许新的锁请求跳转到锁的等待集之前,前提是请求时锁可用。这背后的理由是,如果存在大量争用,在等待集中选择并恢复挂起线程及其实际运行时间之前,可能会有一些(潜在的显著)延迟。因此,来自线程的任何传入锁请求都可以利用这个时间延迟立即运行,希望在恢复的线程准备运行时它已经释放了锁。例如,考虑以下事件序列:

  • 先前已获取monitor X调用notify()的线程A
  • 等待监视器X的线程B已选择挂起(任意)
  • 线程C试图获取监视器X,发现它可用并获取它
  • 线程C运行(尽管线程B当前正在恢复)
  • 线程C在线程B实际运行之前完成执行并释放监视器X
  • 线程B已准备好运行,因此它获取锁并开始执行
  • 很明显,在步骤2和步骤6之间存在一段时间间隔,没有线程实际使用锁。线程C进入并利用时间间隔作为优化。当然,这样做的缺点是在线程B准备运行时没有释放锁的风险,此时线程B将注意到锁不可用,并将再次进入等待集。然而,从统计上可以证明,在大多数情况下,非公平锁定提供了更好的性能

    另请注意,您可以使用公平锁,等待的线程按照获得锁的顺序恢复,但实际上这会提供更差的性能。请在此处阅读更多信息:


    我希望这能回答你的问题。

    不,这不是真的。当一个线程调用notify时,一个等待的线程被唤醒(如果这样的线程存在,否则通知将丢失)。可能您的同事想到了“伪通知”,它可以在实际上没有其他线程调用
    notify
    notifyAll
    时唤醒线程。要过滤“虚假通知”,每个
    notify
    调用都应该伴随一些监视对象状态的更改,等待的线程应该检查该状态:

     synchronized void up() {
        counter++;
        notify();
     }
     synchronized void down() {
       while (counter==0) {
          wait();
       }
       counter--;
     }
    

    注意
    down()
    中的检查状态是在调用wait()之前完成的,因为它可能在调用之前更改,并且通知丢失。换句话说,真实信息随对象的状态一起传递,wait/notify only有助于避免轮询。在不更改对象状态的情况下,切勿依赖通知。

    根据Java文档
    对象#notify
    “唤醒正在该对象监视器上等待的单个线程。如果有线程正在等待该对象,则选择其中一个线程进行唤醒。此选择是任意的,由实现自行决定。”-至于选择一个无效的对象,我无法提交,但我想这将归结为锁监视器API的具体实现。我认为您应该让同事多定义一点“正确”和“有效”。我不相信“像这样基本的东西”是行不通的。请提供更多详细信息或理想情况下的代码示例。notify()和notifyAll()之间的选择取决于您是需要单线程线程还是需要等待监视器的所有线程来恢复工作。这显然是由代码应该做的事情驱动的,所以两者都有存在的理由。从这个意义上讲,notifyAll()并不比notify()更健壮。事实上,在通常只有一个线程运行的情况下调用notifyAll()并不会增加应用程序的活力。-1对于同步块外的
    conditionEvaluation()
    :在调用
    conditionEvaluation
    lock.wait()
    之间的时间间隔内,另一个线程更改条件并调用
    notify
    ,但当前线程不会注意到这一点,并且会一直等待。您的模式不太正确。while循环需要在synchronized块内。@Aubin wait释放监视器,但