preface
Java thread communication is to associate multiple independent threads so that threads can communicate with each other. For example, thread A changes the value of an object and notifies thread B so that thread B can know the value changed by thread A. This is thread communication.
Wait/notify mechanism
A thread calls Object’s wait() method and blocks the thread. Another thread calls notify()/notifyAll() of Object, and the thread that blocks wait() continues to execute.
Wai/notify method
methods | instructions |
---|---|
wait() | Description The current thread is blocked and the thread is in WAITING state |
wait(long) | Set thread blocking duration to TIMED_WAITING. Timeout returns if there is no notification within the set time (ms) |
wait(long, int) | Nanosecond set of thread blocking duration |
notify() | Notify the waiting thread on the same object that has performed wait() and acquired the object lock |
notifyAll() | Notifies all waiting threads on the same object |
Conditions for implementing wait/ Notify:
- The wait thread and notify thread must have the same object lock.
- The wait() and notify()/notifyAll() methods must be in Synchronized methods or code blocks.
Since the wait/notify methods are defined in java.lang.Object, they can be used on any Java Object.
Wait method
Before executing wait(), the current thread must have acquired the object lock. It blocks the current thread, enters a wait state, and suspends the thread at the current wait(). Meanwhile, the wait() method releases the acquired object lock immediately after execution.
Let’s look at an example to see if wait() releases the lock.
First look at the code execution without wait() :
package top.ytao.demo.thread.waitnotify;
/** * Created by YangTao */
public class WaitTest {
static Object object = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (object){
System.out.println("Start thread A");
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End thread A"); }},"Thread A").start();
new Thread(() -> {
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
System.out.println("Start thread B");
System.out.println("End thread B"); }},"Thread B").start(); }}Copy the code
Create two threads A and B. First, sleep after thread B is created to ensure that thread B prints before thread A executes. After the object lock is acquired, thread A will sleep for A period longer than thread B.
The execution result is as follows:
As can be seen from the preceding figure, thread B must wait for thread A to complete the Synchronize code block and release the lock before thread A acquires the lock and moves it into the Synchronize code block. The thread.sleep () method also does not release the lock during this process.
Currently, A thread synchronizes its wait() method to release the object lock. The code for thread A is as follows:
new Thread(() -> {
synchronized (object){
System.out.println("Start thread A");
try {
// Invoke the wait method for object
object.wait();
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End thread A"); }},"Thread A").start();
Copy the code
Execution Result:
At the same time, thread A is always blocked and will not print the end thread A.
The wait(long) method sets the timeout period. If the timeout period is longer than the timeout period, the code after the wait(long) method continues to execute.
new Thread(() -> {
synchronized (object){
System.out.println("Start thread A");
try {
object.wait(1000);
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End thread A"); }},"Thread A").start();
Copy the code
The execution result
Similarly, wait(long, int), like wait(long), is just a set of nanosecond times.
Notify method
Similarly, the current thread must have acquired the thread lock before notify() can be executed. When notify() is called, a blocking waiting thread that has performed wait() is notified to regain the object lock and proceed with the code following wait(). However, unlike wait(), the lock is not released immediately after notify() but only after the code block or method synchronize has executed. Therefore, the notified thread does not acquire the lock immediately. You also need to wait for the notify() thread to release the lock before acquiring it.
notify()
Here is an example of using notify() to implement a complete wait/notify and verify that the notify() thread immediately releases the lock and the wait() thread immediately obtains the lock.
package top.ytao.demo.thread.waitnotify;
/** * Created by YangTao */
public class WaitNotifyTest {
static Object object = new Object();
public static void main(String[] args) {
System.out.println();
new Thread(() -> {
synchronized (object){
System.out.println("Start thread A");
try {
object.wait();
System.out.println("Thread A reobtains the lock and continues.");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End thread A"); }},"Thread A").start();
new Thread(() -> {
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
System.out.println("Start thread B");
object.notify();
System.out.println("Thread B has finished notifying thread A");
try {
Thread A can obtain the lock immediately after notify()
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End thread B"); }},"Thread B").start(); }}Copy the code
Thread A executes wait() and thread B executes notify().
As can be seen from the execution result, after thread B executes notify(), thread A does not obtain the lock even after it sleeps. It can be seen that the notify() method does not release the lock.
Notify () notifies the waiting thread, but only one notify() call can notify the waiting thread executing the wait(). If there are multiple threads in the wait state, notify() is called multiple times, and the order of notification to the thread is based on the order in which the wait() method is executed.
Here is the code to create a thread with two wait() methods:
package top.ytao.demo.thread.waitnotify;
/** * Created by YangTao */
public class MultiWaitNotifyTest {
static Object object = new Object();
public static void main(String[] args) {
System.out.println();
new Thread(() -> {
synchronized (object){
System.out.println("Start thread A");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End thread A"); }},"Thread A").start();
new Thread(() -> {
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
System.out.println("Start thread B");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End thread B"); }},"Thread B").start();
new Thread(() -> {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
System.out.println("Start notification thread C");
object.notify();
object.notify();
System.out.println("End notification thread C"); }},Thread "C").start(); }}Copy the code
Thread A executes wait(), thread B executes wait(), and thread C calls notify() twice.
notifyAll()
NotifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifynotify notify() notifyAll notifyNotify () notifyAll notifyNotify () notifyAll notifyNotify () notifyAll notifyNotify () notifyNotify ()) notifyNotify () notifyNotify ()) notifyNotify ()
Instead of calling notify() multiple times, the C thread calls notifyAll() once.
new Thread(() -> {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
System.out.println("Start notification thread C");
object.notifyAll();
System.out.println("End notification thread C"); }},Thread "C").start();
Copy the code
Execution Result:
The wake order of notifyAll() varies depending on the JVM implementation. In the current test environment, notifyAll() wakes up threads in reverse order.
Realize the producer-consumer model
The production-consumer pattern is that one thread produces data for storage and another thread extracts and consumes data. The producer generates a UUID and stores it in the List object. The consumer reads the data in the List object and deletes it after reading.
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 the data generated by the producer
static List<String> list = new ArrayList<>();
public static void main(String[] args) {
new Thread(() -> {
while (true) {synchronized (list){
// Check whether there is data in the list. 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, generate data to add to the listlist.add(UUID.randomUUID().toString()); list.notify(); System.out.println(Thread.currentThread().getName() + list); }}},"Producer thread A").start();
new Thread(() -> {
while (true) {synchronized (list){
// If there is no data in the list, the system will wait until it receives a notification of data
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();
// Clear the current UUID data after readinglist.clear(); }}},"Consumer thread B").start(); }}Copy the code
Running results:
When the producer thread runs, if there is already unconsumed data, the current thread enters the wait state, receives notification, indicating that the data has been consumed, and continues to add data to the list.
When the consumer thread runs, if there is no unconsumed data, the current thread enters the wait state. After receiving the notification, it indicates that new data has been added to the List, and continues to execute the code to consume the data and clear it.
Based on the object lock, only one thread can obtain it at a time. If the producer obtains the lock, it verifies whether the data needs to be generated, and if the consumer obtains the lock, it verifies whether there is any data to consume.
A simple producer-consumer model is achieved.
conclusion
Wait/notification mechanism is a way to realize communication between Java threads. In multi-threading, each independent running thread communicates with each other to complete work more efficiently and utilize CPU processing program more efficiently. This is also a must for learning or researching Java threads.
Recommended reading
Java Threading Basics, starting with this article
JDK Dynamic Proxy and CGLIB Dynamic Proxy you must Know
Dubbo Series
Pay attention to the public number [Ytao], more original good articles