This article is participating in the “Java Theme Month – Java Brush questions punch card”, see the activity link for details

whywaitThe method has to be theresynchronizedProtect the synchronization code used in?

The wait method is annotated as follows:

# “wait method should always be used in a loop:
 synchronized (obj) {
     while(condition does not hold) obj.wait(); .// Perform action appropriate to condition
}

# This method should only be called by a thread that is the owner of this object's monitor."Copy the code

The wait method should be used in a block of synchronized protected while code, and always determine whether the execution condition is met. If so, proceed; if not, execute the wait method. A monitor lock, known as a synchronized lock, must be held on an object before the wait method can be executed.


Why is it designed this way? What are the benefits of this design?

Conversely, if the wait method was not required to be used in synchronized protected code, but could be called at will, it would be possible to write code like this:

class BlockingQueue {
    Queue<String> buffer = new LinkedList<String>();
    public void offer(String data) {
        buffer.add(data);
        // Since someone may be waiting in take
        notify();  
    }
    
    public String take(a) throws InterruptedException {
        while (buffer.isEmpty()) {

            wait();
        }
        returnbuffer.remove(); }}Copy the code

There are two methods in the code:

  1. offerMethod responsible forbufferTo add data, execute after addingnotifyMethod to wake up a previously waiting thread
  2. takeMethod is responsible for checking the wholebufferWhether it is empty, if it is empty, it enters wait, if it is not empty, it takes out a data.

But this code is not protected by synchronized, and the following scenarios can occur:

  1. First, the consumer thread callstakeMethod and judgmentbuffer.isEmptyDoes the method returntrueIf, fortrueOn behalf ofbufferNull, the thread wants to enter the wait, but is called in the threadwaitMethod was paused by the scheduler before it was executedwaitMethods.
  2. At this point the producer starts running and executes the entireofferMethod, it goes tobufferTo add data and executenotifyMethods, butnotifyDoes not have any effect as the consumer threadswaitMethod was not executed in time, so no thread is waiting to be woken up.
  3. At this point, the consumer thread that was suspended by the scheduler returns to resume executionwaitMethod and enter the wait.

Rewrite the code in the form of the synchronized protected block required by the source comments as follows:

public void offer(String data) {
   synchronized (this) { buffer.add(data); notify(); }}public String take(a) throws InterruptedException {
   synchronized (this) {
    while (buffer.isEmpty()) {
         wait();
       }
     returnbuffer.remove(); }}Copy the code

This ensures that notify will never be called between buffer.isEmpty and wait, improving the security of the program.

In addition, the WAIT method releases the monitor lock, which requires that you first enter synchronized to hold the lock.

There is also the problem of spurious wakeup, where threads can be woken up undesired without either notify/notifyAll, interrupt, or timeout. Although the probability of false wake up is very small in actual production, the program still needs to ensure the correctness of false wake up, so the structure of while loop is needed.

while (condition does not hold)
    obj.wait();
Copy the code

In this way, the condition in the while will be checked again even if it is falsely aroused. If the condition is not met, the wait will continue, thus eliminating the risk of falsely aroused.