6

首先,这几乎是重复的: 如何区分等待(长时间超时)退出通知或超时?

但这是一个新的后续问题。

有这个等待声明:

public final native void wait(long timeout) throws InterruptedException;

它可能会因 InterruptedException 或超时而退出,或者因为在另一个线程中调用了 Notify/NotifyAll 方法,异常很容易捕获但是......

我的代码绝对需要知道退出是来自超时还是通知。(以后需要重新设计这段代码,但现在做不到。所以我需要知道退出等待的原因。)

具体来说,有人可以举一个使用仅在 notify() 上设置为 true 的 ThreadLocal 布尔值的示例,并且所有这些都在现有循环中,如下所示?(这或多或少是另一个线程中接受的答案,但没有给出具体的代码示例。我对 Java 不是很熟悉,所以我需要一个具体的代码示例——最好是在下面现有代码的上下文中.)

public synchronized int getLastSequenceNumber() {
    while (empty) {
        try {
            wait(waitTimeValue);
        } catch (InterruptedException e) {}
    }
    empty = true;
    return reportedSequenceNumber;
}
public synchronized void reconcileLastSequenceNumber(int sequenceNumber) {
    empty = false;
    this.reportedSequenceNumber = sequenceNumber;
    notifyAll();
}

布尔“空”的用途超出了我在这里提出的具体问题。我相信我需要添加另一个布尔值来满足原始问题的建议答案。我如何将该提议的解决方案集成到上面的现有代码片段中?谢谢。

4

4 回答 4

8

Condition使用(及其await方法)而不是内置监视器可能会更好,因为await返回的boolean值指示等待是否超时。

即便如此,您也必须提防虚假唤醒(这与对 . 的调用无法区分signal。)

于 2011-05-24T17:26:43.913 回答
3

无论如何,您应该像现在一样使用循环,无论是否知道wait超时 - 部分原因是虚假唤醒的可能性。但是,我完全不确定您是否真的需要知道呼叫是否由于通知而退出。

考虑通知发生在超时前一纳秒的情况与通知发生在超时后一纳秒的情况。两者之间有什么有用的区别?从根本上说,如果两者“大约同时”发生,则存在竞争条件。

据我所知,wait()真的不会让你知道调用是否超时,但它不应该影响你的代码。无论如何,您应该循环和测试其他作为通知副作用的东西。

老实说,我不清楚 aThreadLocal会在哪里发挥作用 -如果您需要能够从等待线程中判断通知线程是否已达到某个点,这与您想要的完全相反。我认为您根本不需要额外的变量-您empty很好。

于 2011-05-24T17:26:25.530 回答
1

使用内置监视器 API 无法直接报告此情况,但您可以使用wait()明确跟踪此情况的新实现(未经测试)替换 the 和其他函数:

private int wait_ct = 0, signal_ct = 0;

public void checkedNotifyAll() {
  synchronized {
    signal_ct = wait_ct;
    notifyAll();
  }
}

public void checkedNotify() {
  synchronized {
    signal_ct++;
    if (signal_ct > wait_ct)
      signal_ct = wait_ct;
    notify();
}

// Returns true if awoken via notify
public boolean waitChecked(long timeout, int nanos) throws InterruptedException {
  synchronized(this) {
    try {
      wait_ct++;
      super.wait(timeout, nanos);
      if (signal_ct > 0) {
        signal_ct--;
        return true;
      }
      return false;
    } finally {
      wait_ct--;
      if (signal_ct > wait_ct) signal_ct = wait_ct;
      notify(); // in case we picked up the notify but also were interrupted
    }
}

// Note: Do not combine this with normal wait()s and notify()s; if they pick up the signal themselves
// the signal_ct will remain signalled even though the checkedWait()s haven't been
// awoken, potentially resulting in incorrect results in the event of a spurious wakeup

当然,这不一定是一个好方法。notify()毕竟,如果您在调用之前超时,则信号条件可能会丢失。你真的应该在一个循环中等待,检查一些持久的条件。

于 2011-05-24T17:30:13.893 回答
1

这是基于 Jenkov 的信号类的扩展版本。如果它不以 . 结尾,则会引发异常Notify。当我遇到同样的问题时,认为它可能会有所帮助。

public class MonitorObject{
 }

 public class Signal{

     MonitorObject myMonitorObject = new MonitorObject();
     boolean wasSignalled = false;

     public void doWait(int timeOut) throws InterruptedException,TimeoutException{
         synchronized(myMonitorObject){
             long startTime = System.currentTimeMillis();
             long endTime = startTime + timeOut;
             Log.d(TAG, String.format("MonitorStart time %d",startTime));

             while(!wasSignalled){
                 long waitTime = endTime - System.currentTimeMillis();
                 if(waitTime > 0) 
                     myMonitorObject.wait(waitTime);        
                 else{
                     Log.e(TAG, String.format("Monitor Exit timeout error"));
                     throw new TimeoutException();
                 }       
             }

             Log.d(TAG, String.format("MonitorLoop Exit currentTime=%d EndTime=%d",System.currentTimeMillis(),startTime + timeOut));
             //Spurious signal so clear signal and continue running.
             wasSignalled = false;
         }
     }

     public void doNotify(){
         synchronized(myMonitorObject){
             wasSignalled = true;
             myMonitorObject.notify();
         }
     }
 } 
于 2014-02-05T16:03:33.127 回答