Java监视器:如何知道等待(长超时)是由超时还是由Notify()结束?

| 首先,这几乎是以下内容的重复: 如何区分等待(长时间超时)退出通知或超时的时间? 但这是一个新的后续问题。 具有以下等待声明:
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();
}
布尔值\“ empty \”的作用超出了我在此处提出的特定问题。我相信我将需要添加另一个布尔值来满足原始问题的建议答案。我如何将建议的解决方案集成到上面的现有代码段中?谢谢。     
已邀请:
使用
Condition
(及其
await
方法)而不是内置监视器可能会更好,因为
await
返回的
boolean
值表示等待是否超时。 即使这样,您也必须提防虚假唤醒(与调用
signal
并没有区别。)     
无论如何,无论是否知道
wait
是否超时,都应该像现在一样使用循环-部分是由于可能会产生虚假唤醒。但是,我完全不确定您是否真的需要知道呼叫是否由于通知而退出。 考虑通知发生在超时之前一纳秒的情况与通知发生在超时之后一纳秒的情况。两者之间的有用区别是什么?如果这两者在“大约同一时间”发生,则从根本上说存在竞争条件。 据我所知,ѭ8确实不能告诉您调用是否超时,但这不会影响您的代码。无论如何,您应该循环并测试通知的副作用。 坦白说,我不清楚ѭ9会在哪里发挥作用-这与您想要的相反,如果您需要能够从等待线程中知道通知线程是否已到达某一点。我认为您根本不需要一个额外的变量-您的10英镑就可以了。     
没有直接的方法可以使用内置的监视器API来报告此问题,但是您可以用一个明确跟踪(未经测试)的新实现替换
wait()
和其他功能:
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
当然,这不一定是一个好方法。如果恰好在调用before13ѭ之前超时,则信号条件可能会丢失。您确实应该在循环中等待,检查一些持久性状况。     
这是基于詹科夫信号类别的扩展版本。如果不以ѭ14结尾,则会引发异常。以为我遇到同样的问题可能会有所帮助。
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();
         }
     }
 } 
    

要回复问题请先登录注册