wait/notify mechanism for Java thread communication

Preface

Java thread communication is the process of associating individual threads so that threads can communicate with each other.For example, thread A modifies the value of an object and then notifies thread B so that thread B knows the value modified by thread A, which is thread communication.

<!-- more -->

wait/notify mechanism

One thread calls the Object's wait() method to block its thread; the other thread calls the Object's notify()/notifyAll() method, and the wait() blocked thread continues execution.

wai/notify method

Method

Explain

wait()

The current thread is blocked and the thread enters the WAITING state

wait(long)

Set the thread blocking time, the thread will enter TIMED_WAITING status.Timeout returns if there is no notification within the set time (milliseconds)

wait(long, int)

Thread blocking duration settings at nanosecond level

notify()

Notifies a waiting thread on the same object that the wait() method has been executed and that the object lock has been acquired

notifyAll()

Notify all waiting threads on the same object

Conditions for implementing the wait/notify mechanism:

The calling wait thread and the notify thread must have the same object lock.

The wait() method and notify()/notifyAll() method must be in the Synchronized method or code block.

Since the wait/notify method is defined inJava.lang.Object, so it can be used on any Java object.

wait method

The current thread must have acquired an object lock before executing the wait() method.When it is called, the current thread is blocked, entered a wait state, and suspended at the current wait ().At the same time, once the wait() method executes, the acquired object lock is immediately released.

Below is a case study of wait() releasing a lock.

First, review the code execution when you are not using the wait() method:

Create threads A and B.Sleep is first created after the B thread, ensuring that the printing of the B thread is executed after the A thread.In thread A, the sleep time is longer than that of thread B when the object lock is acquired.

The result of execution is:

From the result of the figure above, you can see that the B thread must wait until the A thread executes the synchronize code block and releases the object lock before the A thread acquires the object lock and enters the synchronize code block.During this process, the Thread.sleep() method also does not release the lock.

When the wait() method is currently executed in the A-thread synchronize code block, the object lock is actively released, and the A-thread code is as follows:

Similarly, the wait(long, int) method, like wait(long), is only a time setting at multiple nanosecond levels.

notify method

Similarly, the current thread must have acquired a thread lock before executing the notify() method.When the notify () method is called, a blocked wait thread executing the wait() method is notified, causing the wait thread to regain the object lock and continue executing the code following wait().However, unlike the wait() method, an object lock is not released immediately after executing notify (), but only after executing a code block or method of synchronization, so the thread receiving the notification does not get the lock immediately, nor does it need to wait for the thread executing the notify () method to release the lock before it is acquired.

notify()

The following is the use of the notify() method, which implements a complete wait/notify example, and verifies that the thread executing the notify() method immediately releases the lock after notification is given, and the thread executing the wait() method immediately acquires the lock.

The above A thread executes the wait() method and the B thread executes the notify() method, resulting in:

As you can see from the execution results, after the B thread executes the notify() method, the A thread does not acquire the lock even if it sleep s, and the notify() method does not release the lock.

Notfy () is a thread that notifies a waiting thread, but only one waiting thread that executes the wait() method can be notified by calling the notify() method once.If there are multiple waiting threads, the notify() method needs to be called multiple times, and the order of notifications to threads is based on the order in which the wait() method is executed.

First the A thread executes the wait() method, then the B thread executes the wait() method, and finally the C thread calls the notify() method twice to execute the result:

notifyAll()

Notify threads of multiple wait states. The scenario implemented by calling notify() method multiple times makes the implementation less friendly in practice. If you want to notify threads of all wait states, you can use the notifyAll() method to wake up all threads.

To achieve this, simply change the part of the C thread that called the notify() method more than once to call the notifyAll() method once.

The wake-up order of notifyAll() varies depending on the implementation of different JVM s. Threads are waked up in reverse order in the current test environment.

Implementing a producer-consumer model

The production consumer mode is where one thread produces data for storage and another thread consumes data for extraction.The following two threads are simulated. The producer generates a UUID to store in the List object. The consumer reads the data in the List object and clears it when it is finished.

The implementation code is as follows:

package top.ytao.demo.thread.waitnotify;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Created by YangTao
*/
public class WaitNotifyModelTest {
// Store data generated by producers
static List<String> list = new ArrayList<>();
public static void main(String[] args) {
new Thread(() -> {
while (true){
synchronized (list){
// Determine if there is data in the list, and if there is data, wait until the data is consumed
if (list.size() != 0){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// When there is no data in the list, the generated data is added to the list
list.add(UUID.randomUUID().toString());
list.notify();
System.out.println(Thread.currentThread().getName() + list);
}
}
}, "Producer Threads A ").start();
new Thread(() -> {
while (true){
synchronized (list){
// If there is no data in the list, it goes into a wait state and waits until a data notification is received before continuing
if (list.size() == 0){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Read data when there is data
System.out.println(Thread.currentThread().getName() + list);
list.notify();
// Read, clear the current UUID data
list.clear();
}
}
}, "Consumer Threads B ").start();
}
}

Run result:

When the producer thread is running, if there is already unconsumed data, the current thread enters a wait state, receives notification that the data has been consumed, and then continues to add data to the list.

When the consumer thread runs, if there is no unconsumed data, the current thread enters a waiting state, receives notification that new data has been added to the List, continues executing code consumption data, and clears.

For both producers and consumers, based on object locks, only one thread can acquire them at a time. If a producer acquires a lock, it verifies whether data needs to be generated. If a consumer acquires a lock, it verifies whether data is consumable.

A simple producer-consumer model is done.

summary

Wait/Notify mechanism is a way to achieve communication between Java threads. In multithreads, individual running threads communicate with each other to work together more efficiently and make more efficient use of CPU handlers.This is also a must for learning or studying Java threads.