Why Should Wait() Always Be Called Inside a Loop

Why are wait while-loop conditions safe in a multithreaded context?

From the docs for wait():

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

(emphasis mine)

In other words, without a synchronized block, your code will throw an IllegalMonitorStateException. With synchronized, on the other hand, your condition will be checked atomically with the wait(); call.

This does not automatically mean you have no problems, because "atomically" here is only with regard to the object you're synchronizing on (in your case this), and only relative to other synchronized access to that object. If your condition depends on different objects, or you access the object without synchronization somewhere else, things might go bad. Thus, don't do that.


The same reasoning goes for the use of Locks and Conditions. See this code sample. Condition variables are derived from lock objects, and synchronized/wait/notify is analogous to lock;unlock/await/signal.

wait() is a if block or a while block

It is possible that wait() returns even though the object that is being waited on was not actively notified by you (this is called "spurious wakeup" in the documentation). That's why it makes more sense to wrap the wait()-call into a while loop that checks whether the actual condition that you want to wait for is satisfied and calls wait() again if it's not, rather than simply assuming that when wait() returns the event you are actually waiting for has occured.

What is happening when calling wait() method

For a thread to call wait() or notify(), the thread has to be the owner of the lock for that object.

Otherwise, a runtime error occur and the rest of code is not executed.

When the thread waits, it temporarily releases the lock for other threads to use

In more details, call to wait() does the following:

  • the lock is released
  • current thread is registered as waiting in the monitor
  • processor switches to some other thread ready for execution

Then, some thread calls notify() or notifyAll(), which causes one or all threads which are registered as waiting at this monitor to be moved from the wait set to the ready set, waiting for a free processor to execute.

but it will need it again to continue execution.

This means the execution of the thread is continued with executing synchronized statement to regain the lock. After the lock is aquired, then the wait() method returns. wait(timeout) differs in that except for notify() or notifyAll(), it also can return upon the timeout.

In sum, you need to understand how a thread switches between following 4 states:

  • running on a processor
  • blocked on synchronized statement
  • waiting for notification
  • ready to execute and waiting for a free processor

how to decide the looping condition for wait() in Java

Here, maybe this few lines will push you in the right direction, as a follow up to my previous comments.

class LastPrintedMonitor {
public boolean wasLastEven = false;

}

class PrinterOdd implements Runnable {

LastPrintedMonitor monitor;

public PrinterOdd(LastPrintedMonitor monitor) {
this.monitor = monitor;
}

@Override
public void run() {
for (int i = 2; i < 40; i += 2) {
synchronized (monitor) {
while (!monitor.wasLastEven) {
try {
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

System.out.println(i);
monitor.wasLastEven = false;
monitor.notifyAll();
}
}

}

}

class PrinterEven implements Runnable {

LastPrintedMonitor monitor;

public PrinterEven(LastPrintedMonitor monitor) {
this.monitor = monitor;
}

@Override
public void run() {
for (int i = 1; i < 40; i += 2) {
synchronized (monitor) {
while (monitor.wasLastEven) {
try {
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

System.out.println(i);
monitor.wasLastEven = true;
monitor.notifyAll();
}
}

}

}

public class EvenOddPrinterDemo {

public static void main(String[] args) {
LastPrintedMonitor monitor = new LastPrintedMonitor();

Thread odd = new Thread(new PrinterOdd(monitor));
Thread even = new Thread(new PrinterEven(monitor));

odd.start();
even.start();

try {
odd.join();
even.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("Done!");

}

}

You mentioned two classes so their synchronized methods will not be synchronized to each other. That's why we synchronized on monitor, as there has to be something that those two objects share to make them "hear" each other.

Difference between wait() vs sleep() in Java

A wait can be "woken up" by another thread calling notify on the monitor which is being waited on whereas a sleep cannot. Also a wait (and notify) must happen in a block synchronized on the monitor object whereas sleep does not:

Object mon = ...;
synchronized (mon) {
mon.wait();
}

At this point the currently executing thread waits and releases the monitor. Another thread may do

synchronized (mon) { mon.notify(); }

(on the same mon object) and the first thread (assuming it is the only thread waiting on the monitor) will wake up.

You can also call notifyAll if more than one thread is waiting on the monitor – this will wake all of them up. However, only one of the threads will be able to grab the monitor (remember that the wait is in a synchronized block) and carry on – the others will then be blocked until they can acquire the monitor's lock.

Another point is that you call wait on Object itself (i.e. you wait on an object's monitor) whereas you call sleep on Thread.

Yet another point is that you can get spurious wakeups from wait (i.e. the thread which is waiting resumes for no apparent reason). You should always wait whilst spinning on some condition as follows:

synchronized {
while (!condition) { mon.wait(); }
}

Lock released after calling wait() method?

Yes, T1 will release the lock when wait, and it has to re-aquire the lock after getting notified. See the details in java language specification.

And, the wait method should be called in a while loop.

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

Is it right to use std::condition_variable::wait inside a while loop?

You use it in a loop, and you rely on notify().

The problem is that a condition variable is allowed to wake up "spuriously", that is, without being signaled. That makes it easier to implement, but it requires you to check that you're really where you think you are. So you write a loop:

std::unique_lock<std::mutex> lk(some_mutex);
while (condition_not_satisfied())
cond_var.wait(lk);

Where some_mutex provides a critical area for the variable(s) used in the condition.

Or, as Slava points out, you can use the predicate version:

std::unique_lock<std::mutex> lk(some_mutex);
cond_var.wait(lk, some_callable_object_that_checks_the_predicate);

(I've never liked that form, so I tend to forget about it)

Why must wait() always be in synchronized block

A wait() only makes sense when there is also a notify(), so it's always about communication between threads, and that needs synchronization to work correctly. One could argue that this should be implicit, but that would not really help, for the following reason:

Semantically, you never just wait(). You need some condition to be satsified, and if it is not, you wait until it is. So what you really do is

if(!condition){
wait();
}

But the condition is being set by a separate thread, so in order to have this work correctly you need synchronization.

A couple more things wrong with it, where just because your thread quit waiting doesn't mean the condition you are looking for is true:

  • You can get spurious wakeups (meaning that a thread can wake up from waiting without ever having received a notification), or

  • The condition can get set, but a third thread makes the condition false again by the time the waiting thread wakes up (and reacquires the monitor).

To deal with these cases what you really need is always some variation of this:

synchronized(lock){
while(!condition){
lock.wait();
}
}

Better yet, don't mess with the synchronization primitives at all and work with the abstractions offered in the java.util.concurrent packages.



Related Topics



Leave a reply



Submit