1. Wait/notification mechanism

1.1 Inter-thread communication without wait/notification mechanism

public class MyList {

    volatile private List list = new ArrayList();

    public void add(a) {
        list.add("item");
    }

    public int getSize(a) {
        returnlist.size(); }}public class ThreadA extends Thread {

    private MyList myList;

    public ThreadA(MyList myList) {
        this.myList = myList;
    }

    @Override
    public void run(a) {
        try {
            for (int i = 0; i <10; i++) { myList.add(); System.out.println("myList add i=" + i);
                Thread.sleep(500); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class ThreadB extends Thread {

    private MyList myList;

    public ThreadB(MyList myList) {
        this.myList = myList;
    }

    @Override
    public void run(a) {
        try {
            while (true) {
                if (myList.getSize() == 5) {
                    System.out.println(Thread.currentThread().getName() + " out");
                    throw newInterruptedException(); }}}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) {
        MyList myList = new MyList();
        ThreadA threadA = new ThreadA(myList);
        ThreadB threadB = newThreadB(myList); threadA.start(); threadB.start(); }}Copy the code
  • Although the two threads communicate, ThreadB keeps passing throughwhileThe statement round-robin mechanism checks whether conditions are met, wasting CPU resources
  • If the polling interval is small, CPU resources are wasted. If the polling interval is long, the target data may not be obtained

1.2 Implementation of wait/notification mechanism

  • wait()Method: The thread calling this method releases the lock on the shared resource, exits from the run state, and is queued until awakened again
  • notify()Method: Randomly wake up a thread waiting for the same shared resource in the wait queue, and make the thread exit the wait queue and enter the running state
  • notifyAll()Method: Make all threads waiting for the same shared resource in the wait queue exit from the wait queue and enter the runnable state. At this point, the thread with the highest priority executes first, or randomly, depending on the IMPLEMENTATION of the JVM VIRTUAL machine
public class MyList {

    volatile private List list = new ArrayList();

    public void add(a) {
        list.add("item");
    }

    public int getSize(a) {
        returnlist.size(); }}public class ThreadA extends Thread {

    private MyList myList;

    private Object lock;

    public ThreadA(MyList myList, Object lock) {
        this.myList = myList;
        this.lock = lock;
    }

    @Override
    public void run(a) {
        try {
            synchronized (lock) {
                for (int i = 0; i <10; i++) { myList.add();if (myList.getSize() == 5) {
                        System.out.println("notify ThreadB");
                        lock.notify();
                    }
                    System.out.println("myList add i=" + i);
                    Thread.sleep(500); }}}catch(InterruptedException e) { e.printStackTrace(); }}}public class ThreadB extends Thread {

    private MyList myList;

    private Object lock;

    public ThreadB(MyList myList, Object lock) {
        this.myList = myList;
        this.lock = lock;
    }

    @Override
    public void run(a) {
        try {
            synchronized (lock) {
                if(myList.getSize() ! =5) {
                    System.out.println(Thread.currentThread().getName() + " wait begin " + System.currentTimeMillis());
                    lock.wait();
                    System.out.println(Thread.currentThread().getName() + " wait end "+ System.currentTimeMillis()); }}}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyList myList = new MyList();
        Object lock = new Object();
        ThreadA threadA = new ThreadA(myList, lock);
        ThreadB threadB = new ThreadB(myList, lock);

        threadB.start();
        Thread.sleep(50); threadA.start(); }}Copy the code

The notify() and notifyAll() methods do not release locks immediately after they are executed. The lock is released only after the synchronized code block is executed

1.3 When an Interrupt method encounters a wait method

When a thread is in the wait() state, calling the interrupt() method on a thread object raises an exception

public class Service {

    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait");
                lock.wait();
                System.out.println("end wait"); }}catch (InterruptedException e) {
            System.out.println("catch InterruptedException"); e.printStackTrace(); }}}public class ThreadA extends Thread {

    private Object lock;

    public ThreadA(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run(a) {
        Service service = newService(); service.testMethod(lock); }}public class Test {

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        ThreadA threadA = new ThreadA(lock);
        threadA.start();
        Thread.sleep(5000); threadA.interrupt(); }}Copy the code

1.4 Use of wait(Long)

The wait(long) method is used to wait for a certain amount of time to wake up the lock, and then wake up automatically

public class RunA implements Runnable {

    private Object lock = new Object();

    @Override
    public void run(a) {
        try {
            synchronized (lock) {
                System.out.println("begin wait time=" + System.currentTimeMillis());
                lock.wait(5000);
                System.out.println("end wait time="+ System.currentTimeMillis()); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) {
        Thread thread = new Thread(newRunA()); thread.start(); }}Copy the code

1.5 Producer/Consumer Model (one-to-one)

public class ValueObject {

    volatile public static List myList = new ArrayList();
}

public class C {
    
    public void get(Object lock) {
        try {
            synchronized (lock) {
                if (ValueObject.myList.size() == 0) {
                    lock.wait();
                }
                Object item = ValueObject.myList.remove(0);
                System.out.println(Thread.currentThread().getName() + " get "+ item); lock.notify(); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class P {

    private static int i;

    public void set(Object lock) {
        try {
            synchronized (lock) {
                if(ValueObject.myList.size() ! =0) {
                    lock.wait();
                }
                Object item = "item " + (++i);
                ValueObject.myList.add(item);
                System.out.println(Thread.currentThread().getName() + " set "+ item); lock.notify(); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) {
        final Object lock = new Object();

        Runnable runnable1 = new Runnable() {
            @Override
            public void run(a) {
                C c = new C();
                while (true) { c.get(lock); }}}; Runnable runnable2 =new Runnable() {
            @Override
            public void run(a) {
                P p = new P();
                while (true) { p.set(lock); }}}; Thread c =new Thread(runnable1, "consumer");
        Thread p = new Thread(runnable2, "producer"); c.start(); p.start(); }}Copy the code

1.6 Producer/Consumer Model (many-to-many)

public class ValueObject {

    volatile public static List myList = new ArrayList();
}

public class C {

    public void get(Object lock) {
        try {
            synchronized (lock) {
                while (ValueObject.myList.size() == 0) {
                    lock.wait();
                }
                Object item = ValueObject.myList.remove(0);
                System.out.println(Thread.currentThread().getName() + " get "+ item); lock.notifyAll(); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class P {

    volatile private static int i;

    public void set(Object lock) {

        try {
            synchronized (lock) {
                while(ValueObject.myList.size() ! =0) {
                    lock.wait();
                }
                Object item = "item " + (++i);
                ValueObject.myList.add(item);
                System.out.println(Thread.currentThread().getName() + " set "+ item); lock.notifyAll(); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) {
        final Object lock = new Object();
        final C c = new C();
        final P p = new P();

        Runnable runnable1 = new Runnable() {
            @Override
            public void run(a) {
                while (true) { c.get(lock); }}}; Runnable runnable2 =new Runnable() {
            @Override
            public void run(a) {
                while (true) { p.set(lock); }}}; Thread[] cArrays =new Thread[5];
        Thread[] pArrays = new Thread[5];
        for (int i = 0; i <5; i++) { cArrays[i] =new Thread(runnable1, "consumer-" + i);
            pArrays[i] = new Thread(runnable2, "producer-" + i);
        }
        for (int i = 0; i <5; i++) { cArrays[i].start(); pArrays[i].start(); }}}Copy the code

1.7 Inter-thread communication through pipes: byte stream

In Java, you can use pipe Streams to communicate between different threads

  • PipedOutputStream and PipedInputStream

    public class WriteData {
    
        public void writeMethod(PipedOutputStream outputStream) {
            try {
                System.out.println("write begin");
                for (int i = 0; i <100; i++) { String data = String.valueOf(i +1);
                    outputStream.write(data.getBytes());
                    System.out.println(data);
                }
                outputStream.close();
                System.out.println("write end");
            } catch(IOException e) { e.printStackTrace(); }}}public class ReadData {
    
        public void readMethod(PipedInputStream inputStream) {
            try {
                System.out.println("read begin");
                byte[] byteArray = new byte[20];
                int readLength = inputStream.read(byteArray);
                while(readLength ! = -1) {
                    String newData = new String(byteArray, 0, readLength);
                    System.out.println(newData);
                    readLength = inputStream.read(byteArray);
                }
                inputStream.close();
                System.out.println("read end");
            } catch(IOException e) { e.printStackTrace(); }}}public class ThreadA extends Thread {
    
        private PipedOutputStream outputStream;
    
        private WriteData writeData;
    
        public ThreadA(PipedOutputStream outputStream, WriteData writeData) {
            this.outputStream = outputStream;
            this.writeData = writeData;
        }
    
        @Override
        public void run(a) { writeData.writeMethod(outputStream); }}public class ThreadB extends Thread {
    
        private PipedInputStream inputStream;
    
        private ReadData readData;
    
        public ThreadB(PipedInputStream inputStream, ReadData readData) {
            this.inputStream = inputStream;
            this.readData = readData;
        }
    
        @Override
        public void run(a) { readData.readMethod(inputStream); }}public class Test {
    
        public static void main(String[] args) throws IOException, InterruptedException {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
    
            PipedOutputStream outputStream = new PipedOutputStream();
            PipedInputStream inputStream = new PipedInputStream();
            outputStream.connect(inputStream);
    
            ThreadB threadB = new ThreadB(inputStream, readData);
            threadB.start();
            ThreadA threadA = newThreadA(outputStream, writeData); threadA.start(); }}Copy the code
  • PipedWriter and PipedReader

    public class WriteData {
    
        public void writeMethod(PipedWriter pipedWriter) {
            try {
                System.out.println("write begin");
                for (int i = 0; i <100; i++) { String data = String.valueOf(i +1);
                    pipedWriter.write(data);
                    System.out.println(data);
                }
                pipedWriter.close();
                System.out.println("write end");
            } catch(IOException e) { e.printStackTrace(); }}}public class ReadData {
    
        public void readMethod(PipedReader pipedReader) {
            try {
                System.out.println("read begin");
                char[] charArray = new char[20];
                int readLength = pipedReader.read(charArray);
                while(readLength ! = -1) {
                    String newData = new String(charArray, 0, readLength);
                    System.out.println(newData);
                    readLength = pipedReader.read(charArray);
                }
                pipedReader.close();
                System.out.println("read end");
            } catch(IOException e) { e.printStackTrace(); }}}public class ThreadA extends Thread {
    
        private PipedWriter pipedWriter;
    
        private WriteData writeData;
    
        public ThreadA(PipedWriter pipedWriter, WriteData writeData) {
            this.pipedWriter = pipedWriter;
            this.writeData = writeData;
        }
    
        @Override
        public void run(a) { writeData.writeMethod(pipedWriter); }}public class ThreadB extends Thread {
    
        private PipedReader pipedReader;
    
        private ReadData readData;
    
        public ThreadB(PipedReader pipedReader, ReadData readData) {
            this.pipedReader = pipedReader;
            this.readData = readData;
        }
    
        @Override
        public void run(a) { readData.readMethod(pipedReader); }}public class Test {
    
        public static void main(String[] args) throws IOException, InterruptedException {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
    
            PipedWriter pipedWriter = new PipedWriter();
            PipedReader pipedReader = new PipedReader();
            pipedWriter.connect(pipedReader);
    
            ThreadB threadB = new ThreadB(pipedReader, readData);
            threadB.start();
            ThreadA threadA = newThreadA(pipedWriter, writeData); threadA.start(); }}Copy the code

2. The join method

The join() method causes the thread x to execute the run() task normally, while the current thread Z blocks indefinitely, waiting for thread X to destroy before continuing to execute the code following thread Z

The difference between JOIN and synchronized: Join uses wait() internally, while synchronized uses the “object monitor” principle

2.1 Use of join() method

public class MyThread extends Thread {

    @Override
    public void run(a) {
        try {
            System.out.println("MyThread run begin");
            Thread.sleep(5000);
            System.out.println("MyThread run end");
        } catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        myThread.join();
        System.out.println("main end"); }}Copy the code

2.2 Join () method and exception

public class ThreadA extends Thread {

    @Override
    public void run(a) {
        try {
            System.out.println("MyThread run begin");
            Thread.sleep(5000);
            System.out.println("MyThread run end");
        } catch(InterruptedException e) { e.printStackTrace(); }}}public class ThreadB extends Thread {

    @Override
    public void run(a) {
        try {
            ThreadA a = new ThreadA();
            a.start();
            a.join();
            System.out.println("ThreadB after a.join()");
        } catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) throws InterruptedException {
        ThreadB b = new ThreadB();
        b.start();
        Thread.sleep(500);
        b.interrupt();
        System.out.println("main after b.interrupt()"); }}Copy the code

2.3 Use of join(long) method

The arguments in the JOIN (long) method set the wait time, which automatically continues the code following the current thread

public class MyThread extends Thread {

    @Override
    public void run(a) {
        try {
            System.out.println("MyThread run begin time=" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("MyThread run end time=" + System.currentTimeMillis());
        } catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        myThread.join(2000);
        System.out.println("main end time="+ System.currentTimeMillis()); }}Copy the code

2.4 Differences between join(long) and sleep(long) methods

  • The thread.sleep (long) method does not release the lock

  • The join(long) method is implemented internally using the wait(long) method, all with lock release features

    public class ThreadA extends Thread {
    
        @Override
        public void run(a) {
            try {
                System.out.println("ThreadA run begin time=" + System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("ThreadA run end time=" + System.currentTimeMillis());
            } catch (InterruptedException e) {
    
            }
        }
    
        synchronized public void printMethod(a) {
            System.out.println("ThreadA printMethod enter time="+ System.currentTimeMillis()); }}public class ThreadB extends Thread {
    
        private ThreadA threadA;
    
        public ThreadB(ThreadA threadA) {
            this.threadA = threadA;
        }
    
        @Override
        public void run(a) {
            try {
                synchronized (threadA) {
                    threadA.start();
                    Thread.sleep(6000);
    // threadA.join(6000);}}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            ThreadA threadA = new ThreadA();
            ThreadB threadB = new ThreadB(threadA);
            threadB.start();
            Thread.sleep(10); threadA.printMethod(); }}Copy the code

3. Ref;

The ThreadLocal class allows each thread to have its own shared variable

3.1 Use of the ThreadLocal class

public class Tools {

    public static ThreadLocal t1 = new ThreadLocal();
}

public class ThreadA extends Thread {

    @Override
    public void run(a) {
        try {
            for (int i = 0; i <50; i++) { Tools.t1.set("ThreadA i=" + i);
                System.out.println(Tools.t1.get());
                Thread.sleep(200); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class ThreadB extends Thread {

    @Override
    public void run(a) {
        try {
            for (int i = 0; i <50; i++) { Tools.t1.set("ThreadB i=" + i);
                System.out.println(Tools.t1.get());
                Thread.sleep(200); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) throws InterruptedException {
        ThreadA threadA = new ThreadA();
        ThreadB threadB = new ThreadB();
        threadA.start();
        threadB.start();

        for (int i = 0; i <50; i++) { Tools.t1.set("main i=" + i);
            System.out.println(Tools.t1.get());
            Thread.sleep(200); }}}Copy the code

3.2 Get () returns NULL

Inherit the ThreadLocal class and override the initialValue() method

public class MyThreadLocal extends ThreadLocal {

    @Override
    protected Object initialValue(a) {
        return "defaultValue"; }}public class Test {

    public static MyThreadLocal t1 = new MyThreadLocal();

    public static void main(String[] args) {
        if(t1.get() ! =null) {
            System.out.println("never set value");
            t1.set("setValue"); } System.out.println(t1.get()); }}Copy the code

4 InheritableThreadLocal class

Use the InheritableThreadLocal class to get values inherited from the parent in the child thread

When using the InheritableThreadLocal class, note that if the parent thread changes the InheritableThreadLocal value while the child thread is taking a value, the child thread is still taking an old value

4.1 value inheritance

public class Tools {

    public static InheritableThreadLocal t1 = new InheritableThreadLocal();
}

public class ThreadA extends Thread {

    @Override
    public void run(a) {
        System.out.println("ThreadA value="+ Tools.t1.get()); }}public class Test {

    public static void main(String[] args) throws InterruptedException {
        Tools.t1.set("mainValue");
        System.out.println("main value=" + Tools.t1.get());

        Thread.sleep(500);

        ThreadA threadA = newThreadA(); threadA.start(); }}Copy the code

4.2 Value inheritance and modification

InheritableThreadLocal and override the childValue() method

public class InheritableThreadLocalExt extends InheritableThreadLocal {

    @Override
    protected Object childValue(Object parentValue) {
        return parentValue + " childValue"; }}public class Tools {

    public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}

public class ThreadA extends Thread {

    @Override
    public void run(a) {
        System.out.println("ThreadA value="+ Tools.t1.get()); }}public class Test {

    public static void main(String[] args) throws InterruptedException {
        Tools.t1.set("mainValue");
        System.out.println("main value=" + Tools.t1.get());

        Thread.sleep(500);

        ThreadA threadA = newThreadA(); threadA.start(); }}Copy the code

4.3 validation

public class InheritableThreadLocalExt extends InheritableThreadLocal {

    @Override
    protected Object initialValue(a) {
        return "defaultValue"; }}public class Tools {

    public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}

public class ThreadA extends Thread {

    @Override
    public void run(a) {
        try {
            for (int i = 1; i <=20; i++) { System.out.println("ThreadA value=" + Tools.t1.get());
                Thread.sleep(200); }}catch(InterruptedException e) { e.printStackTrace(); }}}public class Test {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("main before set value=" + Tools.t1.get());

        ThreadA threadA = new ThreadA();
        threadA.start();

        Thread.sleep(2000);
        Tools.t1.set("setValue");
        System.out.println("main after set value="+ Tools.t1.get()); }}Copy the code

Learn from “Java Multi-threaded Programming Core Technology”