I've got a Java thread does 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 like this:
if (val == null) {
LOG.error("null value");
} else {
synchronized (lock) {
nextVal = newVal;
lock.notify();
}
}
Occasionally (literally once every couple of million times) nextVal will be set to null. I've tossed in logging messages and I can see that the order of execution looks like this:
I've explicitly checked and the lock is waking up a second time, it's not being interrupted.
Am I doing something wrong here?
You can't get a Thread to notify itself since it is blocked in wait() . You can have another thread notify the thread by synchronizing on the same object that the thread is locking on and calling notify() . See the below code for a sample. That said, I'd recommend using a BlockingQueue to share data in this respect.
It will go into runnable state. There's never a guarantee a thread will be executing at a particular moment. But you can set the thread's priority to give it a better chance at getting CPU time. Save this answer.
The notify() method chooses one thread that is waiting on the monitor held by the current thread and wakes it up. Typically, the waiting thread will grab the monitor and proceed.
Thread. sleep() method can be used to pause the execution of current thread for specified time in milliseconds. The argument value for milliseconds can't be negative, else it throws IllegalArgumentException .
Yup, Thread
s spontaneously wake up. The this is explicitly stated in the Javadoc:
"A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup."
You need to wait
in a loop. This is also explicitly mentioned in the 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'...
}
Spurious wake ups are fairly common and hence it's always advised to wait()
on a condition within a loop.
Change your code as follows:
while (nextVal == null) {
try {
lock.wait();
} catch (InterruptedException ignored) {
}
}
Specific to the code you've shared: while
also helps you avoid the unnecessary overhead of releasing and re-acquiring the same lock when your code hits continue;
References:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#wait()
From the
public final void wait()
documentation:
... interrupts and spurious wakeups 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
}
This could be a spurious wakeup, but that is not the only possible cause. It is definitely a problem with your logic. You need to put the wait inside a loop that retests for the condition.
When a thread wakes up from the wait, it doesn't have the lock anymore. It released the lock when it started waiting, it needs to reacquire the lock before it can proceed. The just-awoken thread may typically be next in line due to thread affinity (which is probably why your code works most of the time), but there is still the chance it's not; another thread can come in and snag the lock, do its thing and leave the nextVal null, before the awoken thread can take the lock. That means the test for null that the thread made before waiting is no longer relevant. You have to go back and test again once you have the lock.
Change the code to use a loop, like:
synchronized(lock) {
while (nextVal == null) {
lock.wait();
}
...
This way the test is made while the thread has the lock, and whatever happens in the block below the while loop can be certain nextVal really isn't null.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With