Java Monitors: How to know if wait(long timeout) ended by timeout or by Notify()?

↘锁芯ラ 提交于 2019-12-22 06:02:48

问题


First, this is a near duplicate of: How to differentiate when wait(long timeout) exit for notify or timeout?

But it is a new follow-on question.

Having this wait declaration:

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

It could exit by InterruptedException, or by timeout, or because Notify/NotifyAll method was called in another thread, Exception is easy to catch but...

My code absolutely needs to know if the exit was from timeout or notify. (In the future, this code needs to be redesigned, but that cannot be done now. So I need to know the reason for the exit from wait.)

Specifically, can someone give an example of using a ThreadLocal Boolean that is set to true only on notify() and where all this is inside an existing loop as shown below? (This was more or less the accepted answer in the other thread, but no specific code example was given. I'm not all that familiar with Java, so I need a specific code example -- ideally in the context of the existing code below.)

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();
}

the Boolean "empty" serves a purpose outside of the specific question I'm asking here. I believe I will need to add another Boolean to fulfill the suggested answer from the original question. How would I integrate that proposed solution into the existing code snippet above? Thanks.


回答1:


You might be better off using a Condition (and its await method) rather than built-in monitors, because await returns a boolean value indicating whether the wait timed out.

And even then, you must beware of spurious wakeup (which is indistinguishable from a call to signal.)




回答2:


You should be using a loop as you currently are anyway, regardless of knowing whether the wait timed out - partly due to the possibility of spurious wakeups. However, I'm not at all sure that you really need to know whether the call exited due to notification or not.

Consider the situation where the notification occurs a nanosecond before the timeout vs the situation where the notification occurs a nanosecond after the timeout. What's the useful difference between the two? Fundamentally there's a race condition if the two occur at "about the same time".

As far as I can tell, wait() really doesn't let you tell whether the call timed out or not, but it shouldn't affect your code. You should be looping and testing something else that is a side-effect of the notification anyway.

It's not clear to me where a ThreadLocal would come into play to be honest - that's exactly the opposite of what you want if you need to be able to tell from the waiting thread whether the notifying the thread has reached a certain point. I don't think you need an extra variable at all - your empty is fine.




回答3:


There's no direct way to report this with the builtin monitor API, but you could replace the wait() and other functions with a new implementation that tracks this explicitly (untested):

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

This isn't necessarily a good way to do this, of course; if you timeout just before notify() is called, the signal condition may be lost, after all. You really should be waiting in a loop, checking some persistent condition.




回答4:


This is an expanded version based on Jenkov's signal class. An exception is raised if it does not end with a Notify. Thought it might help as I ran into the same problem.

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();
         }
     }
 } 


来源:https://stackoverflow.com/questions/6114320/java-monitors-how-to-know-if-waitlong-timeout-ended-by-timeout-or-by-notify

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!