Home » Java » Why we must use "while" for checking race condition not "if"

Why we must use "while" for checking race condition not "if"

Posted by: admin December 28, 2021 Leave a comment

Questions:

I read the following code in “Thinking in java”.

synchronized(obj)
    {
        while (condition_not_matched)
        {
            obj.wait();
        }
        //continue
        dosomething();

    }

What I think:
Use “if” is OK, because the “wait” means it must get the obj’s lock monitor, and only one thread can executed here.

(1)Why here use “while (condition)” not “if” ?
(2)What happend when executed “obj.wait()”? Does the currrent thread release the lock of “obj”?

(3)And when another thread executed “obj.notify()”, what happend of the previous thread (Did it refetch the lock of obj or not ?if yes, it must condition_not_matched , so “if” is enough.)
Am I wrong?

Answers:

Using an if check instead of checking repeatedly in a loop is a mistake. There are multiple reasons to use the loop.

One is the “spurious wakeup”, which means the wait method can return without the thread having been notified: it’s not valid to infer, based on the thread exiting the wait method, that it must have gotten notified. This may not happen a lot but it is a possibility that has to be handled.

But the main reason is this one: When your thread waits it releases the lock. When it receives a notification it doesn’t have the lock and has to acquire it again before it can exit the wait method. Just because the thread got notified doesn’t mean it’s next in line to get the lock. If the thread decides what to do based on something that happened when the thread didn’t have ownership of the lock, multiple threads may have had the opportunity to act on the same shared object between the time the notify happened and the time that the thread got the lock, and the state of the shared object may not be what your thread thinks it is. Using a while loop allows the thread to check the condition it’s waiting on again, with the lock held, confirming that the condition is still valid before it proceeds.

###

The need for the loop is explained in the Javadoc for the wait methods:

A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied.

To guard against this, after the wait() call returns, you have to check the condition again, and if it’s false, go back and call wait() again instead of proceeding. The while loop accomplishes that.


When you call wait(), the object’s lock is automatically released while waiting, and then acquired again before the method returns. This means that when another thread calls notify() on the object, the waiting thread can’t immediately resume running, because the notifying thread still holds the object’s lock and the waiting thread has to wait for it to be released. It also means that if there are several waiting threads and you call notifyAll(), the waiting threads can’t all resume at once: one of the threads will get the lock and return from wait(), and when it releases the lock, then another of the threads can acquire it and return from wait(), and so on.

In some cases when multiple waiting threads are involved, a waiting thread may wake up, find that the condition is true, and do some stuff that ends up changing the condition back to false — all while holding the lock. Then, when it releases the lock (e.g. by calling wait() again), the next thread wakes up and finds that the condition is false. In this case, it isn’t a spurious wakeup; the condition really did become true, but then became false again before the thread got a chance to check it.

For example: a producer thread adds several items to a queue and calls notifyAll() to wake up the consumer threads. Each consumer thread takes one item from the queue, then releases the lock while processing the item. But if there are more consumer threads than there were items added to the queue, some of the threads will wake up only to find that the queue is empty, so they just have to go back to waiting again.

Checking the condition in a while loop takes care of this situation in addition to handling spurious wakeups.

###

An if statement checks if an expression is true or false by running once, and then runs the code inside the statement only if it is true.

where as

A while condition continues to execute the code in the while statement untill the expression is true. Moreover while loops are more suitable to be used when you don’t know how many times you may have to loop through the condition.

obj.wait() – causes the current thread to wait until another thread invokes the notify() method or the nofityAll() method for the respective object in this case. In case a timeout was passes as a parameter then the tread would wait till the certain amount of time has elapsed.

obj.notify() would wake up a single thread that was waiting on the respective objects monitor. The awakened thread will proceed only after the current thread relinquishes the lock on the object.