Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 417
scugxl Avatar asked Oct 17 '15 11:10

scugxl


People also ask

Why do we use while statement instead of IF statement in a wait on a condition?

Meaning an if statement gives you once the possibility to do something or not (or something else). Whereas a while loop does things as long as the condition is true. Here in the simple exercises we break the loop after the first try but you can do it far more times e.g.

Why do we need a while loop surrounding the wait?

To guarantee liveness, programs must test the while loop condition before invoking the wait() method. This early test checks whether another thread has already satisfied the condition predicate and sent a notification. Invoking the wait() method after the notification has been sent results in indefinite blocking.

How do you determine race condition?

Basically, people detect race condition problem in two big category methods. The first one is detecting in the compile time, which is also called static detection; the other is detection in the run time, which is called dynamic detecting. Both of these two have shortcomings such as coverage, speed, etc.


2 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.

like image 154
Nathan Hughes Avatar answered Sep 22 '22 01:09

Nathan Hughes


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.

like image 45
Wyzard Avatar answered Sep 22 '22 01:09

Wyzard