Use wait/notify to communicate between threads
API
Obj.wait () causes threads entering the Object monitor to wait in the waitSet
Obj.notify () picks one of the threads on object that is waiting for waitSet to wake up
Obj.notifyall () wakes up all threads on an object that are waiting on waitSet
Methods that belong to an Object must have a lock on that Object before they can be called
import lombok.extern.slf4j.Slf4j;
/ * * *@Author blackcat
* @create 2021/7/25 21:00
* @version: 1.0
* @description:wait, notify, notifyAll API */
@Slf4j
public class ThreadWait {
final static Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
log.info("wait1 enter");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("wait1 out");
}, "wait1").start();
new Thread(() -> {
log.info("wait2 enter");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("wait2 out");
}, "wait2").start();
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
// lock.notify();lock.notifyAll(); }}}The results of the / / notifyAll ()
//21:02:39.005 [wait2] info-wait2 Enter
21:02:39.005 [wait1] info-wait1 Enter
//21:02:41.016 [wait1] info-wait1 out
//21:02:41.016 [wait2] info-wait2 out
The results of the / / notify ()
//21:02:39.005 [wait2] info-wait2 Enter
21:02:39.005 [wait1] info-wait1 Enter
//21:02:41.016 [wait2] info-wait2 out
Copy the code
The principle of
Monitor: The Monitor of an object. Whenever synchronization occurs, the thread creates a Monitor object associated with the current object. Monitor can only be owned by one thread, and the current object is locked.
In the Java virtual machine (HotSpot), Monitor is implemented through ObjectMonitor (c++), which has three important properties
ObjectMonitor() {
_WaitSet = NULL; // Threads in the wait state are added to the _WaitSet
_EntryList = NULL ; // Threads waiting for a lock block are added to the list
_owner = NULL;
_count = 0; // Record the number
}
Copy the code
Monitor has two queues, WaitSet and _EntryList, that store the ObjectWaiter list (all waiting threads are wrapped as ObjectWaiter); The owner Monitor object is first added to the _EntryList. 1. Count +1; 2. ③ The thread calls the wait method, enters the WaitSet, releases the lock, and counts -1. ④ The thread applies for the owner again. ⑤ The thread releases resources and exits after processing.
Protective pause mode
Guarded Suspension: One thread waits for the execution result of another thread
synchronized(lock) {
while{lock.wait(); }/ / work
}
// Another thread
synchronized(lock) {
lock.notifyAll();
}
Copy the code
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/ * * *@Author blackcat
* @create 2021/7/25 21:49
* @version: 1.0
* @description: Protective pause mode */
@Slf4j
public class GuardedObjectTest {
public static void main(String[] args) {
Thread 1 waits for the result of thread 2
GuardedObject object = new GuardedObject(1);
new Thread(() -> {
log.info("begin");
Object response = object.get(2000L);
log.info("end");
}).start();
new Thread(() -> {
log.info("Waiting for results.");
Object response = object.get();
log.info("get response: [{}] size", ((List<String>) response).size());
}).start();
new Thread(() -> {
log.info("Perform download");
try{
List<String> download = Downloader.download();
object.set(download);
}catch(Exception e){ e.printStackTrace(); } }).start(); }}class GuardedObject {
private int id;
private Object response;
public GuardedObject(int id) {
this.id = id;
}
public int getId(a) {
return id;
}
Object get(a) {
synchronized (this) {
while (response == null) {
try {
this.wait();
} catch(InterruptedException e) { e.printStackTrace(); }}returnresponse; }}Object get(Long timeout) {
synchronized (this) {
long begin = System.currentTimeMillis();
long now = 0;
while (response == null) {
timeout = timeout - now;
if (timeout <= 0) {
break;
}
try {
this.wait(timeout);
now = System.currentTimeMillis() - begin;
} catch(InterruptedException e) { e.printStackTrace(); }}returnresponse; }}void set(Object response) {
synchronized (this) {
this.response = response;
this.notifyAll(); }}}// Download the utility class
class Downloader {
public static List<String> download(a) throws IOException {
HttpURLConnection conn = (HttpURLConnection) new URL("https://www.baidu.com/").openConnection();
List<String> lines = new ArrayList<>();
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while((line = reader.readLine()) ! =null) { lines.add(line); }}returnlines; }}Copy the code
Producer-consumer model
import lombok.extern.slf4j.Slf4j;
import java.util.LinkedList;
/ * * *@Author blackcat
* @create 2021/7/25 21:57
* @version: 1.0
* @description: Producer-consumer model */
@Slf4j
public class MessageProduct {
public static void main(String[] args) {
MessageQueue queue = new MessageQueue(2);
for (int i = 0; i < 4; i++) {
int id = i;
new Thread(() -> {
queue.put(new Message(id, "Producer" + id));
},"Producer"+id).start();
}
// One consumer thread to process the result
new Thread(() -> {
while (true) {
Message message = queue.get();
Object response = (Object) message.getMsg();
log.debug("take message({}): [{}] ", message.getId(), response); }},"Consumer").start(); }}class Message {
private int id;
private Object msg;
public Message(int id, Object msg) {
this.id = id;
this.msg = msg;
}
public int getId(a) {
return id;
}
public Object getMsg(a) {
return msg;
}
@Override
public String toString(a) {
return "Message{" +
"id=" + id +
", msg=" + msg +
'} '; }}@Slf4j
class MessageQueue {
private int capacity;
private LinkedList<Message> list = new LinkedList<>();
public MessageQueue(int capacity) {
this.capacity = capacity;
}
public void put(Message msg) {
synchronized (this) {
while (list.size() >= capacity) {
try {
log.debug("put wait");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("{}",msg);
list.add(msg);
this.notifyAll(); }}public Message get(a) {
synchronized (this) {
while (list.isEmpty()) {
try {
log.debug("get wait");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message first = list.removeFirst();
this.notifyAll();
returnfirst; }}}Copy the code
join
import lombok.extern.slf4j.Slf4j;
/ * * *@Author blackcat
* @version: 1.0
* @description: * /
@Slf4j
public class MakeTea {
public static void main(String[] args) {
makeTea();
}
private static void makeTea(a) {
// Wash the kettle --> Boil water
Thread t1 = new Thread(()->{
log.info("Wash the kettle");
sleep(1);
log.info("Boil water.");
sleep(5);
},"Wang");
// Wash the teapot --> wash the cup --> fetch the tea
// The current thread waits for the water to boil before making tea
Thread t2 = new Thread(()->{
log.info("Wash the teapot.");
sleep(1);
log.info("Wash the cup.");
sleep(2);
log.info("Take the tea.");
sleep(1);
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Tea");
},"Wang");
t1.start();
t2.start();
}
private static void sleep(int i ) {
try {
Thread.sleep(i * 1000);
} catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
Threadlocal
The ThreadLocal class in Java lets you create variables that can only be read and written by the same thread.
import lombok.extern.slf4j.Slf4j;
/ * * *@Author blackcat
* @create 2021/7/25 22:16
* @version: 1.0
* @description:ThreadLocalExample
*/
@Slf4j
public class ThreadLocalExample {
public static class MyRunnable implements Runnable {
private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
@Override
public void run(a) {
threadLocal.set( (int) (Math.random() * 100));try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Get the result: {}",threadLocal.get()); }}public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable,"t1");
Thread thread2 = new Thread(runnable,"t2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code
The same ThreadLocal variable that is set in the parent thread cannot be retrieved in the child thread.
import lombok.extern.slf4j.Slf4j;
/ * * *@Author blackcat
* @create2021/7/25 her *@version: 1.0
* @description:ThreadLocal,InheritableThreadLocal
*/
@Slf4j
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static InheritableThreadLocal<String> inThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
// Add the local variable of the main thread to the main thread
threadLocal.set("mainVal");
inThreadLocal.set("inMainVal");
// Create a child thread
Thread thread = new Thread(new Runnable() {
@Override
public void run(a) {
log.info("Local variable value in child thread :{}" ,threadLocal.get());
log.info("In local variable value in child thread :{}",inThreadLocal.get()); }}); thread.start();// Output the values of local variables in the main thread
log.info("Local variable value in main thread :{}" ,threadLocal.get());
log.info("In local variable value in main thread :{}",inThreadLocal.get()); }}Copy the code