Java thread spontaneously waking up

I have a Java thread, something like this:

while (running) { synchronized (lock) { if (nextVal == null) { try { lock.wait(); } catch (InterruptedException ie) { continue; } } val = nextVal; nextVal = null; } ...do stuff with 'val'... } 

Elsewhere, I set the value as follows:

 if (val == null) { LOG.error("null value"); } else { synchronized (lock) { nextVal = newVal; lock.notify(); } } 

Sometimes (literally once a couple of million times) nextVal will be set to zero. I threw registration messages and I see that the order of execution is as follows:

  • thread1 sets nextVal to newVal
  • thread1 calls lock.notify ()
  • thread2 wakes up from lock.wait ()
  • thread2 sets val to nextVal
  • thread2 sets nextVal to null
  • thread2 works with val
  • thread2 calls lock.wait ()
  • thread2 wakes up from lock.wait ()
    • No other thread called lock.notify (), and thread2 was not interrupted
  • thread2 sets val to nextVal (which is null)
  • and etc.

I explicitly checked, and the lock wakes up a second time, and it does not break.

Am I doing something wrong here?

+6
source share
3 answers

Yup, Thread wake up spontaneously. This is explicitly stated in Javadoc :

"A thread can also wake up without notice, interruption or timeout, the so-called false awakening."

You need to wait in a loop. This is also explicitly mentioned in javadoc:

  synchronized (obj) { while (<condition does not hold>) obj.wait(timeout); ... // Perform action appropriate to condition } 

In your case:

 while (running) { synchronized (lock) { while (nextVal == null) { try { lock.wait(); } catch (InterruptedException ie) { //oh well } } val = nextVal; nextVal = null; } ...do stuff with 'val'... } 
+3
source

Fragment wakeups are quite common, and so he always recommends wait() provided within the loop.

Change your code as follows:

 while (nextVal == null) { try { lock.wait(); } catch (InterruptedException ignored) { } } 

Specific for general code: while also helps to avoid unnecessary overhead when releasing and re-buying the same lock when your code ends up with continue;

References:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#wait ()

From the public final void wait() documentation:
... interruptions and false awakenings are possible, and this method should always be used in a loop:

  synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition } 
+1
source

It may be a false awakening, but it is not the only possible reason. This is definitely a problem with your logic. You need to put the wait inside a loop that re-tests the condition.

When a thread wakes up from waiting, it no longer has a lock. He let go of the lock, when he began to wait, he needed to lock the lock again before he could continue. Just an awakened thread, as a rule, may be the next in the line due to the proximity of the thread (which is probably why your code works most of the time), but there is still a chance that this is not so; another thread can enter and lock the lock, do its thing and leave nextVal null before the awakened thread can take the lock. This means that checking for zero, that the thread done before the wait is no longer relevant. You should go back and test again as soon as you have a lock.

Change the code to use a loop, for example:

 synchronized(lock) { while (nextVal == null) { lock.wait(); } ... 

Thus, the test is executed when the thread has a lock, and everything that happens in the block below the while loop can be determined that nextVal is really not null.

+1
source

Source: https://habr.com/ru/post/943739/


All Articles