Java: How can I notify a thread only if it is wait

2019-08-24 01:56发布

问题:

In a first Java thread I have:

while (!isDone) {
    try {
        synchronized (this) {
            wait();
        } 
    } catch (InterruptedException e) {
            e.printStackTrace();
    }
    doSomeVeryLongRunningTask();
}

In another thread, I want to send a notify signal:

synchronized (thr1) {
    thr1.notify();
}

However, if the doSomeVeryLongRunningTask() method is running, I don't want the second thread to get blocked. I only want to notify the first thread if it is waiting so that the second thread can continue it tasks without getting locked.

How might I fix the code above to accomplish this?

回答1:

They problem you want to fix does not exist. synchronized blocks will block the thread only if another thread is already inside a synchronized block synchronizing on the same object. Since your doSomeVeryLongRunningTask() will be called outside the synchronized block the notifying thread will never get blocked if the other thread is inside the doSomeVeryLongRunningTask() method.

But this raises another problem. You seem to be thinking, that wait and notify invocations are always paired. This is not the case, you may call notify as often as you wish without anyone listening to it. It might be also the case that a wait invocation returns “spuriously”, i.e. for no apparent reason. You therefore need to define another “hard condition” which is defined by a state that is modified and checked inside the synchronized block.

E.g. inside the class whose instance you have in your thr1 variable, you can define a boolean flag:

boolean condition;

Then you modify you waiting method like this:

while(!isDone) {
  try {
    synchronized(this) {
      while(!condition) wait();
      if(isDone) break;// skip doSomeVeryLongRunningTask()
      condition=false;
    }
  } catch(InterruptedException e) {
    e.printStackTrace();
  }
  doSomeVeryLongRunningTask();
}

And the notifying code to:

synchronized(thr1) {
  thr1.condition=true;
  thr1.notify();
}

This way your notifying code still won’t get blocked (at least never for a significant time) but the waiting thread will wait for at least one notification to happen within one loop cycle.



回答2:

It seems what is blocking your program is not the notify() (it doesn't block ever) but the two synchronized blocks that are synchronizing on the same object.

I don't think there is a workaround to what you ask. Check this link to know why: http://javarevisited.blogspot.com/2011/05/wait-notify-and-notifyall-in-java.html



回答3:

The notify() call doesn't block. Only wait() blocks. You can call notify even if there isn't another thread waiting, but then make sure your algorithm is correct. If you expect to notify only once, then another thread arriving after the notify will wait() forever.



回答4:

The advised pattern is to use notifyAll() AND to have ALL waiting threads check their wake-up condition each time they are notified AND before starting the first Wait.



回答5:

The synchronized in modern Java is about as fast as --i, because this is about what is internally happening thanks to hardware compareAndSet mechanisms. The only moment this slows down noticeably, is when more than one thread is arriving at the synchronized block and therefore at least one has to wait.