Reputation: 47
I have several threads that does some work, and then must go to sleep/wait for an undetermined time. Later they all need to be waken up and resume their work. I can do this by calling wait()
on an object and then notifyall()
on the same object when they need to resume. When researching this issue i discovered this tutorial: http://tutorials.jenkov.com/java-concurrency/thread-signaling.html
Apparantly it is good practice to guard against missed signals and spurious wakeups by storing the signal inside the signal class and check the signal member variable inside a while loop.
Here is the code example from the tutorial:
public class MonitorObject{
}
public class MyWaitNotify3{
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
//clear signal and continue running.
wasSignalled = false;
}
}
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
}
This code is working, but I need to wake up all threads and not just one. If I replace myMonitorObject.notify();
with myMonitorObject.notifyAll();
that will not work because the first thread that resumes work will set the wasSignalled
flag to false
and all the other threads will be trapped in the while loop.
I have made some changes that will enable me to wake up all threads:
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){
}
}
}
}
public void resetSignal() {
wasSignalled = false;
}
public void doNotifyAll() {
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notifyAll();
}
}
But this is not a very good solution, because now I can't wake up just one thread, and I have to reset the signal after doNotify
before I can use doWait again.
Does anyone have a solution that will enable me to use both notify
or notifyAll
on the threads that is waiting?
And one thing about the example I do not understand, why do I have to use a separate MonitorObject class at all? Why can't I just call wait
and notify
on the MyWaitNotify
class itself?
Like this:
public class WaitNotify {
boolean wasSignalled = false;
public void doWait(){
synchronized(this){
while(!wasSignalled){
try{
wait();
} catch(InterruptedException e){
}
}
}
}
public void resetSignal() {
wasSignalled = false;
}
public void doNotifyAll() {
synchronized(this){
wasSignalled = true;
notifyAll();
}
}
}
This seems to be working, any reason I should not be doing this?
Upvotes: 0
Views: 431
Reputation: 19682
Phaser is a good high-level tool for this kind of use case.
final Phaser phaser = new Phaser(1);
doNotify()
phaser.arrive(); // increase phase
doWait()
int phase = phaser.getPhase();
phaser.awaitAdvance( phase ); // await phase change
synchronized
on a privately owned object has the advantage that nobody else could do synchronized
on it. If you do synchronized(this)
, there is a chance that someone else may also want to use this
as a lock object.
Upvotes: 0
Reputation: 182829
Use a generation integer. When a thread blocks, block until the generation integer changes. Before calling notifyAll
, increment the generation integer.
Upvotes: 1