在调用wait()方法之前在锁对象上调用notify()方法是否可行?

3

我在我的代码中使用了wait()和notify(),有一种情况是执行可以先到达notify(),而另一个线程上的锁对象上实际上并没有调用wait()。我在锁对象中创建了一个名为"isWaitCalled"的标志,并使用它来跳过notify()调用,如果wait()还没有被调用。这个标志"isWaitCalled"是否多余?在某些情况下,在wait之前调用notify()是否可以?

线程A:
synchronized (syncObject) {
          try {
                if (!syncObject.isLoadComplete) {
                    syncObject.isWaitCalled = true;
                    syncObject.wait();
                }
          } catch (Exception ex) {
          }                                              
    }

ThreadB:

synchronized (syncObject) {
          try {
                if (!syncObject.isLoadComplete) {
                  syncObject.isLoadComplete =true;
                   if (syncObject.isWaitCalled) {
                       syncObject.notify();         
                   }                  
               }
          } catch (Exception ex) {
          }                                              
    }

如果一棵树在森林中倒下,但没有人在那里听到它,它会发出声音吗? - erickson
1
我也是这么想的。想确认一下,不想假设。 - arun8
4个回答

4
在没有线程等待的情况下,在对象上调用notify()notifyAll()不会出现特殊问题。特别地,它不会阻塞,并且在这个意义上说,isWaitCalled变量无任何作用。
然而,请注意,通知只对已经在通知被传递时等待的线程产生效果。特别地,调用对象的wait()方法后,若有notify(),线程会阻塞直到下一个通知。因此,标准的等待/通知范式要求线程在等待之前检查是否需要等待。
在其最简单的形式中,线程将检查某些共享变量的值,以确定是否需要等待,以及在从等待返回后是否需要恢复等待。通知线程则需要在发送其通知之前适当地修改该变量。这基本上是与您所提供的相反的过程。

2
在调用wait()之前在锁对象上调用notify(),这样做是否可以?这取决于上下文。表面上看,它没有任何损害。但是,如果你的代码依赖于调用wait()的线程看到特定的通知,那么它将不起作用。通知不会排队。如果在调用notify()时没有等待的东西,通知将被丢弃。然而,你的代码由于其他原因而有问题。
Java wait/notify可能会产生虚假通知,即处于wait()状态的线程可能会在没有特定的notify()或notifyAll()的情况下被唤醒。你的代码假定在A线程中的wait()返回后,loadComplete已经被设置。它不能这样假设...因为存在虚假唤醒。您假设只有一个线程等待加载完成。如果不是这样,那么一个线程将被唤醒,其余线程可能会一直卡住。
最后,像这样捕获和丢弃异常是错误的,有两个原因。你捕获/压制了除Error等之外的任何其他已检查或未检查的异常。你忽略了你应该处理的一个已检查异常。如果A线程或B线程在不幸的时候收到中断,那么事情就会破裂。特别是当线程A认为代码已经加载完毕时,它实际上还没有加载完成。如果你不打算处理中断,那么把它们视为“这应该永远不会发生”的情况...并抛出AssertionError或其他同样致命的东西。
我建议您使用在java.lang.Object javadocs中介绍的实现条件变量的标准模式。使用循环,不要尝试优化不必要的通知情况。如果你感觉有冲动进行优化,那么尝试使用高级并发构造(例如“闩锁”)而不是实现自己的解决方案。如果你过于“创意”,你可能会遇到麻烦。即使你的代码是完美的,你也会给维护你的代码的人带来额外的工作。如果他们遇到神秘的并发错误,他们可能需要从头开始分析你的“创意”解决方案,以确定它是否真正可靠。以下是应该如何实现此功能的方式。如您所见,代码比您的解决方案少,并且更容易理解:
ThreadA:
  synchronized (syncObject) {
      try {
          // The loop deals with spurious wakeups.
          while (!syncObject.isLoadComplete) {
              syncObject.wait();
          }
      } catch (InterruptedException ex) {
           // This is the "easy" way to do it correctly, in the case
           // where interrupts are not designed for.
           throw AssertionError("interrupted unexpectedly");
      }                                              
  }

ThreadB:

  synchronized (syncObject) {
      if (!syncObject.isLoadComplete) {
          syncObject.isLoadComplete = true;
          syncObject.notifyAll();  // use `notify()` only if threadA 
                                   // is guaranteed to be the only waiter.
      }                                              
  }

0

不知何故,您必须知道是否有等待的东西。否则,您无法知道是否调用wait。您正在等待的是“谓词”。在您的情况下,isLoadComplete已经是谓词,因此拥有另一个跟踪是否需要等待的标志是多余的。

只有当isLoadCompletefalse时,您才需要等待。只有在将isLoadComplete设置为true时,才调用notify。您认为您需要更多吗?

在没有线程等待时调用notify是无害的。


0
当你发现自己在使用 wait() 方法时,并且该方法调用不在循环中时,你几乎肯定做错了什么。
循环应该是以下形式的:
while (_whatever_im_waiting_for_to_happen_has_not_happened_)
    wait();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接