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