Concurrent notes (4): Notes on the use of wait / notify / notifyAll method

1: Why must wait be used in synchronized protected synchronization code?

The introduction of wait method in the source code is as follows:

        /**
         * As in the one argument version, interrupts and spurious wakeups are
         * possible, and this method should always be used in a loop:
         * <pre&>
         *     synchronized (obj) {
         *         while (&lt;condition does not hold&>)
         *             obj.wait();
         *         ... // Perform action appropriate to condition
         *     }
         * </pre&>
         * This method should only be called by a thread that is the owner
         * of this object's monitor. See the {@code notify} method for a
         * description of the ways in which a thread can become the owner of
         * a monitor.
         *
         */
        public final void wait() throws InterruptedException {
            wait(0);
        }

That is to say, when using the wait method, you must write the wait method in the synchronized protected while code block, and always judge whether the execution conditions are met. If the execution conditions are met, continue to execute. If not, execute the wait method. Before executing the wait method, you must hold the monitor of the object Lock, also known as synchronized lock .

1.1 what would be the problem of not doing so?

    class BlockingQueue{

        Queue<String&> buffer = new LinkedList<&>();
        public void give(String data){
            buffer.add(data);
            notify();
        }
        public String take() throws InterruptedException {
            while(buffer.isEmpty()){
                wait();
            }
            return buffer.remove();
        }
    }

In the code, we can see that there are two methods. The give method is responsible for adding data to the buffer. After adding, the notify method is executed to wake up the waiting thread. The take method is responsible for checking whether the whole buffer is empty. If it is empty, it will enter the waiting process. If it is not empty, it will take out a data. This is a typical idea of producers and consumers.

If you don’t use the wait () method correctly in the code shown above, what exception might occur?

First, the consumer thread calls the take method and determines whether the buffer.isEmpty Method returns true. If true, it means that the buffer is empty, then the thread wants to wait, but before the thread calls the wait method (while)( buffer.isEmpty After ()), it is suspended by the scheduler, so there is no time to execute the wait method .

At this time, the producer starts to run and executes the whole give method. It adds data to the buffer and executes the notify method, but the notify method has no effect, because the wait method of consumer thread has not been executed in time, so no thread is waiting to be awakened .

At this time, the consumer thread that has just been suspended by the scheduler comes back to continue to execute the wait method and enters the waiting . At this time, the consumer may fall into endless waiting, because it missed the wake-up of notify in the give method.

PS: the scheduler mentioned above pauses threads, because in multithreading, CPU scheduling is based on time slices, and each thread can get a certain amount of time slices. But if the thread runs out of time slices, it will be suspended and give up CPU resources to other threads. The “judgment execution” in the code is not an atomic operation. It may be interrupted in the middle, which is unsafe for the thread. Therefore, the thread may be suspended before calling the wait method.

1.2 correct use of wait() method

    class BlockingQueue{
        Queue<String&> buffer = new LinkedList<&>();
        public void give(String data){
            synchronized (this){
                buffer.add(data);
                notify();
            }
        }
        public String take() throws InterruptedException {
            synchronized (this){
                while(buffer.isEmpty()){
                    wait();
                }
                return buffer.remove();
            }
        }
    }

This ensures that the notify method never runs in the buffer . Isempty and wait methods are called to improve the security of the program. In addition, the wait method will release the monitor lock, which also requires that we must first enter into synchronized to hold the lock.

one . two . The problem of false wake-up can be avoided by using while structure judgment

The thread may not have been notified/NotifyAll is also waked up without interruption or timeout, which we don’t want to see. However, in actual production, the probability of false wake-up is very small, but the program still needs to ensure the correctness of false wake-up, so it needs to adopt the structure of while loop.

while(conditiondoesnothold)
obj.wait();

In this way, even if it is falsely awakened, it will check the conditions in while again. If the conditions are not met, it will continue to wait, which eliminates the risk of false wake-up.

2: Similarities and differences between wait and sleep

2.1 similarities

they all block threads

All of them can respond to interrupt : in the process of waiting, if an interrupt signal is received, they can respond and throw interruptedexception exception.

2.2 differences

The wait method must be used in synchronized protected code, but the sleep method does not have this requirement .

when the sleep method is executed in the synchronization code, the monitor lock will not be released, but when the wait method is executed, the monitor lock will be released actively .

The sleep method requires that a time must be defined, and the time will be actively restored when it is expired. For the wait method without parameters, it means that it will wait forever until it is interrupted or awakened, and it will not be actively restored.

wait/notify/notifyAll is defined in the object class, while sleep is defined in the thread class .

2.2.1 why is wait/notify/notifyAll defined in the object class and sleep defined in the thread class?

First of all, because every object in Java has a lock called monitor monitor, every object can be locked, and there is a location in the object header to store the lock information. This lock is at the object level, not the thread level. Wait/notify/notifyAll are also lock level operations. Their locks belong to objects, so it is most appropriate to define them in the object class, because the object class is the parent of all objects.

Secondly, a thread may hold multiple locks in order to realize the complex logic of cooperation. Since we are asking the current thread to wait for the lock of an object, we should naturally operate the object instead of the thread.

Similar Posts: