This question is not about, whether spurious wakeups actually happen, because this was already discussed in full length here: Do spurious wakeups in Java actually happen? Therefore this is also not about, why I do have to put a loop around my wait
Statement. What this is about:
I would like to construct a case, where the spurious wake-up happens. What I've learned so far in the question linked above is this:
If a Linux process is signaled its waiting threads will each enjoy a nice, hot spurious wakeup.
So it seems this will only work a linux machine, in fact I have Ubuntu 11.04 - 64-Bit. I have written a Java program with one thread waiting for a condition, but without loop and another class on which a thread just waits and get notified by another thread. I thought launching all three threads in one JVM would force the case described above, but it seems like this is not the case.
Has anyone else another idea how to construct such a case in Java?
Spurious wakeup describes a complication in the use of condition variables as provided by certain multithreading APIs such as POSIX Threads and the Windows API. Even after a condition variable appears to have been signaled from a waiting thread's point of view, the condition that was awaited may still be false.
A spurious wakeup happens when a thread wakes up from waiting on a condition variable that's been signaled, only to discover that the condition it was waiting for isn't satisfied. It's called spurious because the thread has seemingly been awakened for no reason.
You can't force a spurious wakeup, but to the running thread, a spurious wakeup is indistinguishable from a regular wakeup (the source of the event is different, but the event itself is the same)
To simulate a spurious wakeup, simply call notify();
Calling interrupt()
isn't suitable, because doing so sets the interrupt flag, and after a spurious wakeup, the interrupt flag is not set
"Spurious wakeup" is a hotchpotch and covers any implementation detail in that realm. Therefore it is quite hard to make out what a "real" spurious wakeup is and why another one is "unreal" - let alone on which layer this implementation detail originates. Choose any one from "kernel", "system library (libc)", "JVM", "Java standart library (rt.jar)" or a custom framework built on top of this stack.
The following program shows a spurious wakeup using java.util.concurrent
stuff:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SpuriousWakeupRWLock {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
static int itemsReady;
public static void main(String[] args) throws Exception {
// let consumer 1 enter condition wait
new ConsumerOne().start();
Thread.sleep(500);
lock.lock();
try {
// let consumer 2 hit the lock
new ConsumerTwo().start();
Thread.sleep(500);
// make condition true and signal one (!) consumer
System.out.println("Producer: fill queue");
itemsReady = 1;
condition.signal();
Thread.sleep(500);
}
finally {
// release lock
lock.unlock();
}
System.out.println("Producer: released lock");
Thread.sleep(500);
}
abstract static class AbstractConsumer extends Thread {
@Override
public void run() {
lock.lock();
try {
consume();
} catch(Exception e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
abstract void consume() throws Exception;
}
static class ConsumerOne extends AbstractConsumer {
@Override
public void consume() throws InterruptedException {
if( itemsReady <= 0 ){ // usually this is "while"
System.out.println("One: Waiting...");
condition.await();
if( itemsReady <= 0 )
System.out.println("One: Spurious Wakeup! Condition NOT true!");
else {
System.out.println("One: Wakeup! Let's work!");
--itemsReady;
}
}
}
}
static class ConsumerTwo extends AbstractConsumer {
@Override
public void consume() {
if( itemsReady <= 0 )
System.out.println("Two: Got lock, but no work!");
else {
System.out.println("Two: Got lock and immediatly start working!");
--itemsReady;
}
}
}
}
Output :
One: Waiting...
Producer: fill queue
Producer: released lock
Two: Got lock and immediatly start working!
One: Spurious Wakeup! Condition NOT true!
The used JDK was:
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.04.2)
OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)
It is based on one implementation detail in java.util.concurrent
: The standard Lock
has one waiting queue, the Condition
has another waiting queue. If the condition is signalled, the signalled thread is moved from the condition's queue into the lock's queue. The implementation detail: It is moved at the end of the queue. If another thread is already waiting in the lock queue and this second thread did not visit the condition variable, this thread can "steal" the signal. If the implementation would have put the first thread before the second thread, this would not have happened. This "bonus" could/would be based on the fact that the first thread has got the lock already once and that the waiting time in the condition associated with the same lock is credited to that thread.
I define this as "spurious" because
The last point is demonstrated with this code using Object.wait()
:
public class SpuriousWakeupObject {
static Object lock = new Object();
static int itemsReady;
public static void main(String[] args) throws Exception {
// let consumer 1 enter condition wait
new ConsumerOne().start();
Thread.sleep(500);
// let consumer 2 hit the lock
synchronized (lock) {
new ConsumerTwo().start();
Thread.sleep(500);
// make condition true and signal one (!) consumer
System.out.println("Producer: fill queue");
itemsReady = 1;
lock.notify();
Thread.sleep(500);
} // release lock
System.out.println("Producer: released lock");
Thread.sleep(500);
}
abstract static class AbstractConsumer extends Thread {
@Override
public void run() {
try {
synchronized(lock){
consume();
}
} catch(Exception e){
e.printStackTrace();
}
}
abstract void consume() throws Exception;
}
static class ConsumerOne extends AbstractConsumer {
@Override
public void consume() throws InterruptedException {
if( itemsReady <= 0 ){ // usually this is "while"
System.out.println("One: Waiting...");
lock.wait();
if( itemsReady <= 0 )
System.out.println("One: Spurious Wakeup! Condition NOT true!");
else {
System.out.println("One: Wakeup! Let's work!");
--itemsReady;
}
}
}
}
static class ConsumerTwo extends AbstractConsumer {
@Override
public void consume() {
if( itemsReady <= 0 )
System.out.println("Two: Got lock, but no work!");
else {
System.out.println("Two: Got lock and immediatly start working!");
--itemsReady;
}
}
}
}
Output:
One: Waiting...
Producer: fill queue
Producer: released lock
One: Wakeup! Let's work!
Two: Got lock, but no work!
Here the implementation seems to do as I would expect it: The thread using the condition is awoken first.
Final note: The idea for the principle comes from Why does java.util.concurrent.ArrayBlockingQueue use 'while' loops instead of 'if' around calls to await()? , although my interpretation is different and the code is from myself.
Original question you've refered (as far, as wikipedia article) says that spurious wakeups happens in linux implementation of pthread, as side effect of process being signalled. From your question it seems to me that you missed up "signal" (which is linux inter-process communication method) with Object.notify() (which is java internal inter-thread communication method).
If you want to observe spurious wakeup -- you must run your java program and try to send it some signal.
I tried a simple test on Linux, by sending a simple Java process signals (such as QUIT, STOP, CONT, etc.). These did not seem to cause a spurious wakeup.
So (at least to me) it's still not clear under what conditions a Linux signal will cause a spurious wakeup in Java.
I found a reproducer that forces spurious wakeups in Java bug 6454029. It starts up 30, 60, and then 100 pairs of waiters/notifiers and causes them to wait and notify a specified number of times. It uses standard Object.wait() and Object.notify() rather than the higher-level Lock and Condition objects. I've managed to use it to cause spurious wakeups to occur on my linux 64 bit machine with the argument value of 1000000 with both java 1.8.0-b132 and 1.6.0_45. Note that the original filer was complaining about Windows XP, so presumably this works on at least one flavor of windows as well.
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