Q Why learn multithreaded communication?

A Each thread has its own private thread thread within the thread, threads do not interfere with each other. To make the best use of server resources, we often need multiple threads to collaborate.

Business target, A, B two threads output in turn

package co.dianjiu.thread;

public class MyThreadNoLock {
    static class MyThreadA implements Runnable{

        @Override
        public void run(a) {
            for (int i = 0; i < 50; i++) {
                System.out.println("MyThreadA===>"+ i); }}}static class MyThreadB implements Runnable{

        @Override
        public void run(a) {
            for (int i = 0; i < 50; i++) {
                System.out.println("MyThreadB===>"+ i); }}}public static void main(String[] args) {
        new Thread(new MyThreadA()).start();
        new Thread(newMyThreadB()).start(); }}Copy the code

The execution result

The execution sequence of thread A and thread B cannot be controlled

.

MyThreadB===>48

MyThreadA===>41

MyThreadB===>49

MyThreadA===>42

MyThreadA===>43

MyThreadA===>44

MyThreadA===>45

MyThreadA===>46

MyThreadA===>47

MyThreadA===>48

MyThreadA===>49

1. Use locks for thread communication

Depending on the thread and lock relationship, only one thread holds the lock at a time.

package co.dianjiu.thread;

public class MyThreadNoLock {

    private static Object lock = new Object();

    static class MyThreadA implements Runnable{

        @Override
        public void run(a) {
            // Object lock to synchronize code blocks
            synchronized (lock){
                for (int i = 0; i < 50; i++) {
                    System.out.println("MyThreadA===>"+ i); }}}}static class MyThreadB implements Runnable{

        @Override
        public void run(a) {
            // Object lock to synchronize code blocks
            synchronized (lock){
                for (int i = 0; i < 50; i++) {
                    System.out.println("MyThreadB===>"+ i); }}}}public static void main(String[] args) {
        new Thread(new MyThreadA()).start();
        new Thread(newMyThreadB()).start(); }}Copy the code

The execution result

You can control A to execute first, then B to execute, and get A little bit closer to the target

.

MyThreadA===>47

MyThreadA===>48

MyThreadA===>49

MyThreadB===>0

MyThreadB===>1

MyThreadB===>2

MyThreadB===>3

.

Use wait notifications for communication

The basis of waiting notification mechanism is that two threads use the same object lock. After printing their own output, A and B wake up the other waiting thread, and then enter the waiting state and release the lock at the same time.

package co.dianjiu.thread;

public class MyThreadNoLock {

    private static Object lock = new Object();

    static class MyThreadA implements Runnable{

        @Override
        public void run(a) {
            synchronized (lock){
                for (int i = 0; i < 50; i++) {
                    try {
                        System.out.println("MyThreadA===>" + i);
                        lock.notify();
                        lock.wait();
                    } catch(InterruptedException e) { e.printStackTrace(); } } lock.notify(); }}}static class MyThreadB implements Runnable{

        @Override
        public void run(a) {
            synchronized (lock){
                for (int i = 0; i < 50; i++) {
                    try {
                        System.out.println("MyThreadB===>" + i);
                        lock.notify();
                        lock.wait();
                    } catch(InterruptedException e) { e.printStackTrace(); } } lock.notify(); }}}public static void main(String[] args) {
        new Thread(new MyThreadA()).start();
        new Thread(newMyThreadB()).start(); }}Copy the code

The execution result

At this point we have achieved our business goals.

.

MyThreadA===>45

MyThreadB===>45

MyThreadA===>46

MyThreadB===>46

MyThreadA===>47

MyThreadB===>47

MyThreadA===>48

MyThreadB===>48

MyThreadA===>49

MyThreadB===>49

Use semaphore for communication

The JDK provides a Semaphore class with “Semaphore” functionality, our own implementation of Semaphore communication based on the volatile keyword.

package co.dianjiu.thread;

public class MyThreadVolatile {
    private static volatile int signal = 0;

    static class MyThreadVolatileA implements Runnable {

        @Override
        public void run(a) {
            while (signal < 50) {
                if(signal % 2= =1) {
                    System.out.println("MyThreadA===>" + signal);
                    synchronized (this) {
                        signal=signal+1;
                    }
                }
            }
        }
    }


    static class MyThreadVolatileB implements Runnable {

        @Override
        public void run(a) {
            while (signal < 50) {
                if(signal % 2= =0) {
                    System.out.println("MyThreadB===>" + signal);
                    synchronized (this) {
                        signal++;
                    }
                }
            }
        }

    }

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

The execution result

We can customize simple semaphore to achieve odd – and even – numbered thread printing.

.

MyThreadA===>43

MyThreadB===>44

MyThreadA===>45

MyThreadB===>46

MyThreadA===>47

MyThreadB===>48

MyThreadA===>49

MyThreadB===>50

4. Use pipe flow for communication

A pipeline stream is typically used for multithreaded IO stream operations.

Character streams PipedWriter, PipedReader

Byte stream PipedOutputStream, PipedInputStream

package co.dianjiu.thread;

import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;

public class MyThreadPipe {
    static class MyReaderThread implements Runnable{
        private PipedReader reader;

        public MyReaderThread(PipedReader reader) {
            this.reader = reader;
        }

        @Override
        public void run(a) {
            System.out.println("MyReaderThread");
            int receive = 0;
            try {
                while((receive = reader.read()) ! = -1) {
                    System.out.print((char)receive); }}catch(IOException e) { e.printStackTrace(); }}}static class MyWriterThread implements Runnable {
        private PipedWriter writer;
        public MyWriterThread(PipedWriter writer) {
            this.writer = writer;
        }
        @Override
        public void run(a) {
            System.out.println("MyWriterThread");
            int receive = 0;
            try {
                writer.write("https://dianjiu.co");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    writer.close();
                } catch(IOException e) { e.printStackTrace(); }}}}public static void main(String[] args) throws IOException, InterruptedException {
        PipedReader reader = new PipedReader();
        PipedWriter writer = new PipedWriter();
        // Create a link
        writer.connect(reader);
        new Thread(new MyReaderThread(reader)).start();
        new Thread(newMyWriterThread(writer)).start(); }}Copy the code

The execution result

MyReaderThread MyWriterThread dianjiu.co

5. In-depth understanding of Join method

The source of the join() method

public final void join(a) throws InterruptedException {
    	// Wait for 0 ms and enter the wait state forever
        join(0);
}
Copy the code

Join (long) method source

Parameter 1 is the number of milliseconds to wait

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
		// The number of milliseconds is a value greater than 0
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
		The number of milliseconds to wait is 0
        if (millis == 0) {
            // use a this.wait call loop conditional on this.isAlive; When the thread terminates, the this.notifyAll method is called.
            while (isAlive()) {
                //join() finally calls wait(0)
                wait(0); }}else {
            // use the this.wait call loop conditional on this.isAlive; When the thread terminates, the this.notifyAll method is called.
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break; } wait(delay); now = System.currentTimeMillis() - base; }}}Copy the code

Join (long, int) method source

Parameter 1 Wait time in milliseconds

Parameter 2 Wait time nanoseconds

public final synchronized void join(long millis, int nanos)
    throws InterruptedException {
	    // The number of milliseconds is a value greater than 0
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
		// The number of nanoseconds ranges from 0 to 999999
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }
		// If the number of nanoseconds is less than 500000 and the number of milliseconds is 0, the wait time is 1 millisecond
    	// If the number of nanoseconds is greater than or equal to 500000 and the number of milliseconds is 0, the wait time is 1 millisecond
    	// If the number of nanoseconds is greater than or equal to 500000 and the number of milliseconds is not 0, the wait time is milliseconds +1
        if (nanos >= 500000|| (nanos ! =0 && millis == 0)) {
            millis++;
        }
		Join (); join(); join();
        join(millis);
    }
Copy the code

Let’s review how it’s used

package co.dianjiu.thread;public class MyThreadJoin extends Thread{    @Override    public void run(a){        try {            System.out.println("Sleep for a second.");            Thread.sleep(1000);            System.out.println("The child thread sleeps for a second.");        } catch(InterruptedException e) { e.printStackTrace(); }}public static void main(String[] args) throws InterruptedException {        MyThreadJoin a = new MyThreadJoin();        a.setName("a");        a.start();        System.out.println(a.getName() + "= = = >" + a.getState());        System.out.println(Thread.currentThread().getName() + "= = = >" + Thread.currentThread().getState());        System.out.println("Main thread, no join method completes first.");    }}
Copy the code

A ===>RUNNABLE main===>RUNNABLE main thread. If no join method is executed, the child thread will finish sleeping for a second first

After adding the join method

package co.dianjiu.thread;public class MyThreadJoin extends Thread{    @Override    public void run(a){        try {            System.out.println("Sleep for a second.");            Thread.sleep(1000);            System.out.println("The child thread sleeps for a second.");        } catch(InterruptedException e) { e.printStackTrace(); }}public static void main(String[] args) throws InterruptedException {        MyThreadJoin a = new MyThreadJoin();        a.setName("a");        a.start();        a.join();        System.out.println(a.getName() + "= = = >" + a.getState());        System.out.println(Thread.currentThread().getName() + "= = = >" + Thread.currentThread().getState());        System.out.println("The main thread, with the join method, will wait for the subthread to finish executing before the main thread executes.");    }}
Copy the code

The child thread TERMINATED main===>RUNNABLE sleeps one second. The primary thread TERMINATED main===>RUNNABLE sleeps one second. The primary thread TERMINATED main===>RUNNABLE sleeps one second

To summarize the function of join() method, calling the join method of Thread will make the current Thread enter the wait state and wait for the completion of join Thread before executing the current Thread.

6. In-depth understanding of Sleep methods

Sleep (long) method source

Parameter 1 millisecond

Public static native void sleep(long millis) throws InterruptedException;
Copy the code

Sleep (long, int) method source

Parameter 1 millisecond

Parameter 2 nanoseconds

public static void sleep(long millis, int nanos)    throws InterruptedException {    	If (millis < 0) {throw new IllegalArgumentException("timeout value is negative"); } / / the number of nanoseconds value range of 0-999999 the if (nanos < 0 | | nanos > 999999) {throw new IllegalArgumentException (" nanosecond timeout value out of range"); } // If the number of nanoseconds is less than 500000 and the number of milliseconds is 0 then the sleep time is 1ms // If the number of nanoseconds is greater than or equal to 500000 and the number of milliseconds is 0 then the sleep time is 1ms // If the number of nanoseconds is greater than or equal to 500000, then the sleep time is 1ms. And milliseconds to 0 is not sleep time of milliseconds + 1 if (nanos > = 500000 | | (nanos! = 0 && millis == 0)) { millis++; Sleep (0) sleep(millis); }
Copy the code

Qsleep, JOIN, yield, wait? (Ali interview question)

Asleep(long) and Yield () are static methods of Thread. They do not release the lock, but release the CPU. The sleep method blocks, and yield returns to the ready state. Wait (), notify(), and notifyAll() are all java.lang.object methods that release the lock and CPU resources. After wait, the thread waits to wake up again (notify randomly, notifyAll wakes up). Automatic wake up at the end of a thread) is put into the lock pool to compete for the synchronization lock; Join () calls wait(), which also releases the lock but does not release the CPU. The current running thread calls another thread’s JOIN method, and the current thread enters the wait pool and waits for the other join thread to complete.

7. Understanding the ThreadLocal class

ThreadLocal is a thread-local variable or thread-local store. Strictly speaking, the ThreadLocal class does not communicate between multiple threads, but rather allows each thread to have its own “independent” variables that do not affect each other.

Eight, InheritableThreadLocal class

[InheritableThreadLocal] The InheritableThreadLocal class is a little different from the ThreadLocal class. It is not only the current thread that can access the replica value, but also its child threads.