Do spurious wakeups in Java actually happen?
JavaMultithreadingLockingSpurious WakeupJava Problem Overview
Seeing various locking related question and (almost) always finding the 'loop because of spurious wakeups' terms1 I wonder, has anyone experienced such kind of a wakeup (assuming a decent hardware/software environment for example)?
I know the term 'spurious' means no apparent reason but what can be the reasons for such kind of an event?
(1 Note: I'm not questioning the looping practice.)
Edit: A helper question (for those who like code samples):
If I have the following program, and I run it:
public class Spurious {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();
lock.lock();
try {
try {
cond.await();
System.out.println("Spurious wakeup!");
} catch (InterruptedException ex) {
System.out.println("Just a regular interrupt.");
}
} finally {
lock.unlock();
}
}
}
What can I do to wake this await
up spuriously without waiting forever for a random event?
Java Solutions
Solution 1 - Java
The Wikipedia article on spurious wakeups has this tidbit:
> The pthread_cond_wait()
function in Linux is implemented using the futex
system call. Each blocking system call on Linux returns abruptly with EINTR
when the process receives a signal. ... pthread_cond_wait()
can't restart the waiting because it may miss a real wakeup in the little time it was outside the futex
system call. This race condition can only be avoided by the caller checking for an invariant. A POSIX signal will therefore generate a spurious wakeup.
Summary: If a Linux process is signaled its waiting threads will each enjoy a nice, hot spurious wakeup.
I buy it. That's an easier pill to swallow than the typically vague "it's for performance" reason often given.
Solution 2 - Java
I have a production system that exhibits this behaviour. A thread waits on a signal that there is a message in the queue. In busy periods, up to 20% of the wakeups are spurious (ie when it wakes there is nothing in the queue). This thread is the only consumer of the messages. It runs on a Linux SLES-10 8-processor box and is built with GCC 4.1.2. The messages come from an external source and are processed asynchronously because there are problems if my system does not read them fast enough.
Solution 3 - Java
To answer the question in the titile - Yes! it does happen.Though the Wiki article mentions a good deal about spurious wakeups a nice explanation for the same that I came across is as follows -
>Just think of it... like any code, thread scheduler may experience temporary blackout due to something abnormal happening in underlying hardware / software. Of course, care should be taken for this to happen as rare as possible, but since there's no such thing as 100% robust software it is reasonable to assume this can happen and take care on the graceful recovery in case if scheduler detects this (eg by observing missing heartbeats). > >Now, how could scheduler recover, taking into account that during blackout it could miss some signals intended to notify waiting threads? If scheduler does nothing, mentioned "unlucky" threads will just hang, waiting forever - to avoid this, scheduler would simply send a signal to all the waiting threads. > >This makes it necessary to establish a "contract" that waiting thread can be notified without a reason. To be precise, there would be a reason - scheduler blackout - but since thread is designed (for a good reason) to be oblivious to scheduler internal implementation details, this reason is likely better to present as "spurious".
I was reading this answer from Source and found it reasonable enough. Also read
Spurious wakeups in Java and how to avoid them.
PS: Above link is to my personal blog that has additional details about spurious wakeups.
Solution 4 - Java
Cameron Purdy wrote a blog post a while back about being hit by the spurious wakeup problem. So yes, it happens
I'm guessing it's in the spec (as a possibility) because of limitations of some of the platforms which Java gets deployed on? although I may be wrong!
Solution 5 - Java
Just to add this. Yes it happens and I spent three days searching for the cause of a multi-threading problem on a 24 core machine (JDK 6). 4 of 10 executions experienced that without any pattern. This never happened on 2 core or 8 cores.
Studied some online material and this is not a Java problem but a general rare but expected behavior.
Solution 6 - Java
Answering the OP's question
> What can I do to wake this await up spuriously without waiting forever > for a random event?
, no any spurious wakeup could wake up this awaiting thread!
Regardless of whether spurious wakeups can or cannot happen on a particular platform, in a case of the OP's snippet it is positively impossible for Condition.await()
to return and to see the line "Spurious wakeup!"
in the output stream.
Unless you are using very exotic Java Class Library
This is because standard, OpenJDK's ReentrantLock
's method newCondition()
returns the AbstractQueuedSynchronizer
's implementation of Condition
interface, nested ConditionObject
(by the way, it is the only implementation of Condition
interface in this class library),
and the ConditionObject
's method await()
itself checks whether the condition does not holds and no any spurious wakeup could force this method to mistakenly return.
By the the way, you could check it yourself as it is pretty easy to emulate spurious wakeup once the AbstractQueuedSynchronizer
-based implementation is involved.
AbstractQueuedSynchronizer
uses low-level LockSupport
's park
and unpark
methods, and if you invoke LockSupport.unpark
on a thread awaiting on Condition
, this action cannot be distinguished from a spurious wakeup.
Slightly refactoring the OP's snippet,
public class Spurious {
private static class AwaitingThread extends Thread {
@Override
public void run() {
Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();
lock.lock();
try {
try {
cond.await();
System.out.println("Spurious wakeup!");
} catch (InterruptedException ex) {
System.out.println("Just a regular interrupt.");
}
} finally {
lock.unlock();
}
}
}
private static final int AMOUNT_OF_SPURIOUS_WAKEUPS = 10;
public static void main(String[] args) throws InterruptedException {
Thread awaitingThread = new AwaitingThread();
awaitingThread.start();
Thread.sleep(10000);
for(int i =0 ; i < AMOUNT_OF_SPURIOUS_WAKEUPS; i++)
LockSupport.unpark(awaitingThread);
Thread.sleep(10000);
if (awaitingThread.isAlive())
System.out.println("Even after " + AMOUNT_OF_SPURIOUS_WAKEUPS + " \"spurious wakeups\" the Condition is stil awaiting");
else
System.out.println("You are using very unusual implementation of java.util.concurrent.locks.Condition");
}
}
, and no matter how hard the unparking(main) thread would try to awake the awaiting thread, the Condition.await()
method will never return in this case.
The spurious wakeups on Condition
's awaiting methods are discussed in the javadoc of Condition
interface .
Although it does say that,
> when waiting upon a Condition, a spurious wakeup is permitted to occur
and that > it is recommended that applications programmers always assume that they can occur and so always wait in a loop.
but it later adds that > An implementation is free to remove the possibility of spurious wakeups
and AbstractQueuedSynchronizer
's implementation of Condition
interface does exactly that - removes any possibility of spurious wakeups.
This surely holds true for other ConditionObject
's awaiting methods.
So, the conclusion is :
we should always call Condition.await
in the loop and check if the condition does not hold, but with standard, OpenJDK, Java Class Library is can never happen.
Unless, again, you use very unusual Java Class Library (which must be very very unusual, because another well-known non-OpenJDK Java Class Libraries, currently almost extinct GNU Classpath and Apache Harmony, seems to have identical to standard implementation of Condition
interface)
Solution 7 - Java
https://stackoverflow.com/a/1461956/14731 contains an excellent explanation of why you need to guard against of spurious wakeups even if the underlying operating system does not trigger them. It is interesting to note that this explanation applies across multiple programming languages, including Java.