Explain java multithreading in simple terms (basic chapter)

Posted by adam87 on Thu, 03 Feb 2022 15:05:17 +0100

1 basic concepts of process and thread

1.1 background of the process

The original computer can only accept some specific instructions. Every time a user inputs an instruction, the computer makes an operation. When the user is thinking or typing, the computer is waiting. This is very inefficient. In many cases, the computer is in a waiting state.

Batch operating system

Later, there was a batch operation system, which wrote down a series of instructions that needed to be operated to form a list and handed it to the computer at one time. The user writes several programs to be executed on the magnetic tape, and then hands them over to the computer to read and execute these programs one by one, and writes the output results on another magnetic tape.

The batch operating system improves the efficiency of the computer to a certain extent, but because the instruction operation mode of the batch operating system is still serial, there is always only one program running in the memory, and the later program needs to wait for the execution of the previous program before it can start, while the previous program is sometimes blocked due to I/O operation, network and other reasons, Therefore, the efficiency of batch operation is not high.

Proposal of process

People have higher and higher requirements for the performance of computers. The existing batch operating system can not meet people's needs. The bottleneck of batch operating system is that there is only one program in memory. Can there be multiple programs in memory? This is an urgent problem to be solved.

Therefore, scientists put forward the concept of process.

Process is the space allocated by the application in memory, that is, the running program. Each process does not interfere with each other. At the same time, the process saves the running state of the program at every moment.

Program: a code set written in a certain programming language (java, python, etc.) that can complete certain tasks or functions. It is an orderly set of instructions and data and a piece of static code.

At this time, the CPU runs the process in the way of time slice rotation: the CPU allocates a time period for each process, which is called its time slice. If the process is still running at the end of the time slice, the operation of the process is suspended and the CPU is allocated to another process (this process is called context switching). If the process blocks or ends before the end of the time slice, the CPU switches immediately without waiting for the time slice to run out.

When the process is suspended, it will save the current process status (process ID, resources used by the process, etc.), and recover according to the previously saved status when switching back next time, and then continue to execute.

The operating system using process + CPU time slice rotation looks like multiple tasks are executed in the same time period. In other words, the process makes the concurrency of the operating system possible. Although there are multiple tasks executing in the macro view of concurrency, in fact, for a single core CPU, there is only one task occupying CPU resources at any specific time.

The requirements for the operating system are further improved

Although the emergence of processes has greatly improved the performance of the operating system, with the passage of time, people are not satisfied that a process can only do one thing at a time. If a process has multiple subtasks, it can only execute these subtasks one by one, which greatly affects the efficiency.

For example, when anti-virus software detects a user's computer, if it gets stuck in a certain detection, the subsequent detection items will also be affected. Or when you use the virus scanning function in the anti-virus software, you can't use the garbage cleaning function in the anti-virus software before the virus scanning is finished, which obviously can't meet people's requirements.

Thread proposal

So can these subtasks be performed at the same time? So people put forward the concept of thread, let a thread execute a subtask, so a process contains multiple threads, and each thread is responsible for a separate subtask.

With threads, things are much simpler. When the user uses the virus scanning function, let the virus scanning thread execute it. At the same time, if the user uses the garbage cleaning function again, you can pause the virus scanning thread first, respond to the user's garbage cleaning operation first, and let the garbage cleaning thread execute it. After the response, switch back, and then execute the virus scanning thread.

Note: how the operating system allocates time slices to each thread involves the thread scheduling strategy. Interested students can take a look at the operating system, which will not be explained in detail in this paper.

In short, the introduction of processes and threads greatly improves the performance provided by operations. Processes make the concurrency of the operating system possible, while threads make the internal concurrency of processes possible.

Multithreading can also achieve concurrency. Why do we use multithreading?

Multi process mode can indeed achieve concurrency, but using multithreading has the following advantages:

The communication between processes is complex, while the communication between threads is simple. Generally, we need to use shared resources, which are easy to communicate between processes.

The process is heavyweight, while the thread is lightweight, so the system overhead of multithreading is less.

The difference between process and thread

A process is an independent running environment, and a thread is a task executed in a process. The essential difference between them is whether they occupy memory address space and other system resources (such as I/O):

Processes occupy a certain memory address space separately, so there is memory isolation between processes, data is separated, data sharing is complex, but synchronization is simple, and each process does not interfere with each other; Threads share the memory address space and resources occupied by their processes. Data sharing is simple, but synchronization is complex.

The process occupies a certain memory address space alone. The problem of one process will not affect other processes and the stability of the main program. It has high reliability; A thread crash may affect the stability of the whole program, and the reliability is low.

The process occupies a certain memory address space alone. The creation and destruction of the process not only needs to save the register and stack information, but also needs the allocation and recycling of resources and page scheduling, which is expensive; Threads only need to save register and stack information, with less overhead.

Another important difference is that the process is the basic unit of resource allocation by the operating system, while the thread is the basic unit of scheduling by the operating system, that is, the unit of CPU allocation time.

1.2 context switching

A thread or context switch is sometimes referred to as a switch from one process to another. Context refers to the contents of CPU registers and program counters at a certain point in time.

Register is a small amount of fast flash memory in cpu. It usually stores and accesses the intermediate value of the calculation process to improve the running speed of computer programs.

The program counter is a special register, which is used to indicate the position where the CPU is executing in the instruction sequence. The stored value is the position of the executing instruction or the position of the next instruction to be executed. The specific implementation depends on the specific system.

Thread a - example B

1. Suspend thread A first and save its state in cpu in memory.

2. Retrieve the context of the next thread B in memory and recover it in the register of CPU to execute thread B.

3. When B finishes executing, restore thread A according to the position pointed in the program counter.

The CPU implements the multithreading mechanism by allocating CPU time slices to each thread. The CPU circularly executes tasks through the time slice allocation algorithm. After the current task executes one time slice, it will switch to the next task.

However, the state of the previous task will be saved before switching, so that the state of this task can be loaded again when switching back to this task next time. Therefore, the process from saving to reloading a task is a context switch.

Context switching is usually computationally intensive, which means that this operation will consume a lot of CPU time, so the more threads, the better. How to reduce the number of context switches in the system is a key issue to improve the performance of multithreading.

reference material

Several state transitions of threads

Origin and difference of processes and threads

Summary of process, thread and multithreading

Concept / identification / structure / status of process

Operating system process concept

Process management notes 1. The concept of process and its background

Context switching

Concept / identification / structure / status of process

Detailed explanation of thread life cycle and state transition

Process and thread

2 Java multithreading entry class and interface

2.1 Thread class and Runnable interface

In the previous chapter, we learned the basic concept of multithreading in the operating system. So how do we use multithreading in Java?

First, we need to have a "Thread" class. JDK provides Thread class and Runnalble interface to let us implement our own "Thread" class.

Inherit the Thread class and override the run method;

Implement the run method of Runnable interface;

2.1.1 inherit Thread class

First learn how to use it, then learn the principle. First, let's see how to write a Java multithreaded program with Thread and Runnable.

The first is to inherit the Thread class:

public class Demo {
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("MyThread");
        }
    }

```java
public static void main(String[] args) {
    Thread myThread = new MyThread();
    myThread.start();
}
```

}

Note that the thread is not started until the start() method is called!

After we call the start() method in the program, the virtual opportunity first creates a thread for us, and then calls the run() method when the thread gets the time slice for the first time.

Note that the start() method cannot be called multiple times. After calling the start() method for the first time, calling the start() method again will throw an exception.

2.1.2 implementation of Runnable interface

Next, let's look at the Runnable interface (JDK 1.8 +):

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

You can see that Runnable is a functional interface, which means that we can use Java 8's functional programming to simplify the code.

Example code:

public class Demo {
    public static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println("MyThread");
        }
    }

    public static void main(String[] args) {
        new MyThread().start();
    
        // Java 8 functional programming, you can omit the MyThread class
        new Thread(() -> {
            System.out.println("Java 8 Anonymous Inner Class ");
        }).start();
    }

}

2.1.3 Thread class construction method

Thread class is an implementation class of Runnable interface. Let's take a look at the source code of thread class.

Check the construction method of Thread class and find that it is actually a simple call to a private init method to initialize. Method signature of init:

// Thread class source code 

// Fragment 1 - init method
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals)

// Fragment 2 - constructor calls init method
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

// Fragment 3 - use the init method to initialize the private property of AccessControlContext type
this.inheritedAccessControlContext = 
    acc != null ? acc : AccessController.getContext();

// Fragment 4 - two pairs of private attributes used to support ThreadLocal
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

Let's explain these parameters of init method one by one:

g: Thread group: specify which thread group this thread is under;

target: specify the task to be executed;

Name: the name of the thread. The names of multiple threads can be repeated. If no name is specified, see segment 2;

acc: see fragment 3, which is used to initialize the private variable inheritedaccesscontrocontext.

This variable is a little magical. It is a private variable, but in the Thread class, only the init method initializes it, and set it to null in the exit method. There is no other place to use it. Generally, we won't use it. When will we use this variable? You can refer to this stackoverflow problem: Restrict permissions to threads which execute third party software

inheritThreadLocals: inheritable ThreadLocal. See fragment 4. There are two private attributes in the Thread class to support ThreadLocal. We will introduce the concept of ThreadLocal in the following chapters.

In practice, most of us directly call the following two construction methods:

Thread(Runnable target)

Thread(Runnable target, String name)


2.1.4 several common methods of thread class

Here are some common methods of Thread class:

currentThread(): a static method that returns a reference to the currently executing thread object;

start(): the method to start executing the thread. The java virtual opportunity calls the run() method in the thread;

yield(): yield means give up in English. Similarly, yield() here means that the current thread is willing to give up the occupation of the current processor. It should be noted here that even if the current thread calls the yield() method, the program may continue to run this thread when scheduling;

sleep(): a static method to make the current thread sleep for a period of time;

join(): make the current thread wait for another thread to execute before continuing. The internal call is implemented by the wait method of Object class;

2.1.5 comparison between thread class and Runnable interface:

To implement a custom Thread class, you can inherit the Thread class or implement the Runnable interface. What are the advantages and disadvantages between them?

Due to the "single inheritance, multiple implementations" feature of Java, the Runnable interface is more flexible than Thread.

The appearance of Runnable interface is more object-oriented, and threads are encapsulated separately.

The appearance of Runnable interface reduces the coupling between thread object and thread task.

If you do not need to use many methods of the Thread class when using threads, it is obviously lighter to use the Runnable interface.

Therefore, we usually give priority to using the method of "implementing Runnable interface" to customize thread classes.

2.2 Callable, Future and FutureTask

Generally speaking, we use Runnable and Thread to create a new Thread. However, they have a disadvantage that the run method has no return value. Sometimes we want to start a Thread to execute a task, and there is a return value after the task is completed.

JDK provides Callable interface and Future class to solve this problem, which is also the so-called "asynchronous" model.

2.2.1 Callable interface

Similar to Runnable, Callable is a functional interface with only one abstract method. The difference is that the methods provided by Callable have return values and support generics.

@FunctionalInterface

public interface Callable<V> {

​    V call() throws Exception;

}

How do you usually use callable? Callable is generally used in conjunction with the thread pool tool ExecutorService. We will explain the use of thread pools in subsequent chapters. This only introduces that ExecutorService can use the submit method to make a callable interface execute. It will return a Future, and our subsequent programs can get the results through the get method of the Future.

Here you can see a simple demo:

public abstract interface Future<V> {
    public abstract boolean cancel(boolean paramBoolean);
    public abstract boolean isCancelled();
    public abstract boolean isDone();
    public abstract V get() throws InterruptedException, ExecutionException;
    public abstract V get(long paramLong, TimeUnit paramTimeUnit)
            throws InterruptedException, ExecutionException, TimeoutException;
}

The cancel method is an attempt to cancel the execution of a thread.

Note that an attempt to cancel may not be successful. Because the task may have been completed or cancelled, or some other factors cannot be cancelled, there is a possibility of cancellation failure. The return value of boolean type means "cancel succeeded". The paramBoolean parameter indicates whether to cancel thread execution by interrupt.

So sometimes, in order to make the task have the function of canceling, Callable is used instead of Runnable. If you use Future for cancellability but do not provide available results, you can declare Future <? > Form type and return null as the result of the underlying task.

2.2.3 FutureTask class

The Future interface is introduced above. This interface has an implementation class called FutureTask. FutureTask implements the RunnableFuture interface, which inherits both the RunnableFuture interface and the Future interface:

// Custom Callable, same as above
class Task implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        // The simulation takes a second
        Thread.sleep(1000);
        return 2;
    }
    public static void main(String args[]){
        // use
        ExecutorService executor = Executors.newCachedThreadPool();
        FutureTask<Integer> futureTask = new FutureTask<>(new Task());
        executor.submit(futureTask);
        System.out.println(futureTask.get());
    }
}

What's the use of FutureTask class? Why have a FutureTask class? As mentioned earlier, Future is just an interface, and its methods such as cancel, get and isDone are very complex to implement by themselves. So JDK provides a FutureTask class for us to use.

Example code:

  • /**
      *
    
      * state Possible state transition paths are as follows:
      * NEW -> COMPLETING -> NORMAL
      * NEW -> COMPLETING -> EXCEPTIONAL
      * NEW -> CANCELLED
      * NEW -> INTERRUPTING -> INTERRUPTED
        */
        private volatile int state;
        private static final int NEW          = 0;
        private static final int COMPLETING   = 1;
        private static final int NORMAL       = 2;
        private static final int EXCEPTIONAL  = 3;
        private static final int CANCELLED    = 4;
        private static final int INTERRUPTING = 5;
        private static final int INTERRUPTED  = 6;
    

State indicates the running state of the task, and the initial state is NEW. The running state will only be terminated in the set, setException and cancel methods. COMPLETING and INTERRUPTING are the transient states after the task is completed.

The above is the introduction of several basic classes and interfaces of Java multithreading. You can open JDK to see the source code and experience the design ideas and uses of these categories!

reference material

Thread state analysis defined by Java language

Java thread state analysis

FutureTask source code analysis

3 thread group and thread priority

3.1 ThreadGroup

In Java, ThreadGroup is used to represent thread group. We can use thread group to control threads in batch.

The relationship between ThreadGroup and Thread is as simple and rough as their literal meaning. Each Thread must exist in a ThreadGroup, and Thread cannot exist independently of ThreadGroup. The name of the Thread executing the main() method is main. If it is not explicitly specified in the new Thread, the Thread group of the parent Thread (the Thread currently executing the new Thread) is set as its own Thread group by default.

Example code:

public class Demo {
    public static void main(String[] args) {
        Thread testThread = new Thread(() -> {
            System.out.println("testThread Current thread group name:" +
                    Thread.currentThread().getThreadGroup().getName());
            System.out.println("testThread Thread Name:" +
                    Thread.currentThread().getName());
        });

        testThread.start();
        System.out.println("implement main Method thread Name:" + Thread.currentThread().getName());
    }

}

Output result:

implement main Method thread Name: main

testThread Current thread group name: main

testThread Thread Name: Thread-0


ThreadGroup manages the threads below it. ThreadGroup is a standard downward reference tree structure. The reason for this design is to prevent "superior" threads from being referenced by "subordinate" threads and being unable to be effectively recycled by GC.

3.2 thread priority

Thread priority in Java can be specified, ranging from 1 to 10. However, not all operating systems support 10 level priority division (for example, some operating systems only support 3-level Division: low, medium and high). Java only gives the operating system a reference value of priority. The final priority of threads in the operating system is determined by the operating system.

Java's default thread priority is 5. The execution order of threads is determined by the scheduler, and the thread priority will be set before the thread is called.

Generally, high priority threads will have a higher probability of execution than low priority threads. We use the setPriority() instance method of the Thread class to set the priority of the Thread.

public class Demo {
    public static void main(String[] args) {
        Thread a = new Thread();
        System.out.println("I am the default thread priority:"+a.getPriority());
        Thread b = new Thread();
        b.setPriority(10);
        System.out.println("The priority I set for threads is:"+b.getPriority());
    }
}

Output result:

I am the default thread priority: 5

I set the thread priority: 10


Since there are 1-10 levels to set the priority of threads, some readers may ask, can I use this method to specify the execution order of some threads during business implementation?

Our answer to this question is: No!

The priority in Java is not particularly reliable. The priority set for threads in Java programs is only a suggestion to the operating system, which may not necessarily be adopted by the operating system. The real call order is determined by the thread scheduling algorithm of the operating system.

Let's verify through the code:

public class Demo {
    public static class T1 extends Thread {
        @Override
        public void run() {
            super.run();
            System.out.println(String.format("The thread currently executing is:%s,Priority:%d",
                    Thread.currentThread().getName(),
                    Thread.currentThread().getPriority()));
        }
    }

    public static void main(String[] args) {
        IntStream.range(1, 10).forEach(i -> {
            Thread thread = new Thread(new T1());
            thread.setPriority(i);
            thread.start();
        });
    }

}

One output:

The thread currently executing is: Thread-17,Priority: 9

The thread currently executing is: Thread-1,Priority: 1

The thread currently executing is: Thread-13,Priority: 7

The thread currently executing is: Thread-11,Priority: 6

The thread currently executing is: Thread-15,Priority: 8

The thread currently executing is: Thread-7,Priority: 4

The thread currently executing is: Thread-9,Priority: 5

The thread currently executing is: Thread-3,Priority: 2

The thread currently executing is: Thread-5,Priority: 3


Java provides a thread scheduler to monitor and control threads in the RUNNABLE state. The thread scheduling strategy adopts preemption, and the thread with high priority will have a greater chance to execute first than the thread with low priority. Under the same priority, the principle of "first come, first served" shall be followed. Each Java program has a default main thread, which is the first thread started through the JVM, the main thread.

There is also a thread called Daemon, which has a low priority by default.

If a thread is a daemon thread, if all non daemon threads end, the daemon thread will also end automatically.

The application scenario is: when all non daemon threads end, the rest of the child threads (daemon threads) are automatically closed, which eliminates the trouble of continuing to close the child threads.

A Thread is a non daemon Thread by default, which can be set through setDaemon(boolean on) of Thread class.

Previously, we talked about that a thread must exist in a thread group. What happens when the priorities of threads and thread groups are inconsistent? Let's use the following case to verify:

public static void main(String[] args) {
    ThreadGroup threadGroup = new ThreadGroup("t1");
    threadGroup.setMaxPriority(6);
    Thread thread = new Thread(threadGroup,"thread");
    thread.setPriority(9);
    System.out.println("I am the priority of the thread group"+threadGroup.getMaxPriority());
    System.out.println("I am the priority of the thread"+thread.getPriority());
}

Output:

I'm priority 6 of the thread group. I'm priority 6 of the thread

Therefore, if the priority of a thread is greater than the maximum priority of the thread group where the thread is located, the priority of the thread will be invalidated and replaced by the maximum priority of the thread group.

3.3 common methods and data structures of thread group

3.3.1 common methods of thread group

Gets the name of the current thread group

Thread.currentThread().getThreadGroup().getName()

Copy thread group

// Copy a thread array to a thread group
Thread[] threads = new Thread[threadGroup.activeCount()];
TheadGroup threadGroup = new ThreadGroup();
threadGroup.enumerate(threads);

Thread group unified exception handling

package com.func.axc.threadgroup;

public class ThreadGroupDemo {
    public static void main(String[] args) {
        ThreadGroup threadGroup1 = new ThreadGroup("group1") {
            // Inherit ThreadGroup and redefine the following methods
            // Throw unchecked exception on thread member
            // This method is executed
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(t.getName() + ": " + e.getMessage());
            }
        };

        // This thread is a member of threadGroup1
        Thread thread1 = new Thread(threadGroup1, new Runnable() {
            public void run() {
                // Throw unchecked exception
                throw new RuntimeException("Test exception");
            }
        });
    
        thread1.start();
    }

}

3.3.2 data structure of thread group

Thread groups can also contain other thread groups, not just threads.

First look at the member variables in the ThreadGroup source code

public class ThreadGroup implements Thread.UncaughtExceptionHandler {
    private final ThreadGroup parent; // Father ThreadGroup
    String name; // Name of ThreadGroupr
    int maxPriority; // Maximum thread priority
    boolean destroyed; // Is it destroyed
    boolean daemon; // Daemon thread
    boolean vmAllowSuspension; // Can I interrupt

    int nUnstartedThreads = 0; // Thread not started
    int nthreads; // Number of threads in ThreadGroup
    Thread threads[]; // Threads in ThreadGroup
    
    int ngroups; // Number of thread groups
    ThreadGroup groups[]; // Thread group array

}

Then look at the constructor:

// private constructors 
private ThreadGroup() { 
    this.name = "system";
    this.maxPriority = Thread.MAX_PRIORITY;
    this.parent = null;
}

// By default, the current ThreadGroup is passed in as the parent ThreadGroup, and the parent thread group of the new thread group is the thread group of the running thread.
public ThreadGroup(String name) {
    this(Thread.currentThread().getThreadGroup(), name);
}

// Constructor
public ThreadGroup(ThreadGroup parent, String name) {
    this(checkParentAccess(parent), parent, name);
}

// Private constructor, primary constructor
private ThreadGroup(Void unused, ThreadGroup parent, String name) {
    this.name = name;
    this.maxPriority = parent.maxPriority;
    this.daemon = parent.daemon;
    this.vmAllowSuspension = parent.vmAllowSuspension;
    this.parent = parent;
    parent.add(this);
}

The third constructor calls the checkParentAccess method. Here is the source code of this method:

// Check parent ThreadGroup
private static Void checkParentAccess(ThreadGroup parent) {
    parent.checkAccess();
    return null;
}

// Judge whether the currently running thread has the permission to modify the thread group
public final void checkAccess() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkAccess(this);
    }
}

This involves the SecurityManager class, which is the security manager of Java. It allows the application to determine what the operation is and whether it is executed in the security context that allows the operation to be executed before executing a potentially unsafe or sensitive operation. The application can or cannot allow this operation.

For example, a third-party class library is introduced, but its security cannot be guaranteed.

In fact, the Thread class also has a checkAccess() method, which is used to determine whether the currently running Thread has permission to modify the called Thread instance. (Determines if the currently running thread has permission to modify this thread.)

To sum up, a thread group is a tree structure. There can be multiple threads or thread groups under each thread group. Thread group can control the priority of threads and check the permissions of threads.

reference material

https://blog.csdn.net/Evankaka/article/details/51627380

Java Concurrent Programming Practice

Core technology of Java multithreaded programming

4 Java thread status and main conversion methods

  • 4 Java thread status and main conversion methods

    4.1 thread state transition in operating system

    First, let's look at thread state transitions in the operating system.

    In the current operating system, threads are regarded as lightweight processes, so the state of operating system threads is actually consistent with that of operating system processes.

    [external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-60rinttt-1643883116959)( https://3803541485-files.gitbook.io/ ~/files/v0/b/gitbook-28427. appspot. com/o/assets%2F-L_ 5HvtIhTFW9TQlOF8e%2F-L_ 5TIKcBFHWPtY3OwUo%2F-L_ 5TJM1VhwmwbNGzqwJ%2F%E7%B3%BB%E7%BB%9F%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE. png? generation=1551665548910935&alt=media)]

    System process / thread conversion diagram

    The operating system thread mainly has the following three states:

    Ready: the thread is waiting to use the CPU and can enter the running state after being called by the scheduler.

    Running: the thread is using the CPU.

    Waiting state: the thread has passed the call of waiting event or is waiting for other resources (such as I/O).

4.2 6 states of java thread

// Thread.State source code
public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

4.2.1 NEW

The Thread in the NEW state was not started at this time. Not started here means that the start() method of the Thread instance has not been called.

private void testStateNew() {
    Thread thread = new Thread(() -> {});
    System.out.println(thread.getState()); // Output NEW 
}

As can be seen from the above, only the thread is created without calling the start() method. At this time, the thread is in the NEW state.

Two extended problems about start()

    Is it feasible to call the start() method of the same thread repeatedly?

    If a thread is finished (in TERMINATED state at this time), is it feasible to call the thread's start() method again?

To analyze these two problems, let's take a look at the source code of start():

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    group.add(this);
    
    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
    
        }
    }

}

We can see that there is a threadStatus variable inside start(). If it is not equal to 0, calling start () will throw an exception directly.

Let's go on to see that there is a native start0() method. There is no handling of threadStatus in this method. Here, we seem to have no way to take this threadStatus. Let's take another look through debug:

@Test
public void testStartMethod() {
    Thread thread = new Thread(() -> {});
    thread.start(); // First call
    thread.start(); // Second call
}

I hit the breakpoint at the beginning of the start() method. Describe the results I saw at the breakpoint:

The value of threadStatus is 0 on the first call.

The value of threadStatus during the second call is not 0.

View the source code of the current thread status:

// Thread.getState method source code:
public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

// sun.misc.VM source code:
public static State toThreadState(int var0) {
    if ((var0 & 4) != 0) {
        return State.RUNNABLE;
    } else if ((var0 & 1024) != 0) {
        return State.BLOCKED;
    } else if ((var0 & 16) != 0) {
        return State.WAITING;
    } else if ((var0 & 32) != 0) {
        return State.TIMED_WAITING;
    } else if ((var0 & 2) != 0) {
        return State.TERMINATED;
    } else {
        return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
    }
}

Therefore, combined with the above source code, we can get the results of two extended problems:

After calling threadStatus() twice, the value of threadStatus() will change, and threadStatus() will throw an exception.

For example, a threadStatus of 2 indicates that the current thread status is TERMINATED.

4.2.2 RUNNABLE

Indicates that the current thread is running. The thread in the RUNNABLE state is running in the Java virtual machine, or it may be waiting for other system resources (such as I/O).

RUNNABLE state of threads in Java

After looking at several states of the operating system Thread, let's take a look at the definition of the RUNNABLE state in the Thread source code:

/**

  • Thread state for a runnable thread. A thread in the runnable
  • state is executing in the Java virtual machine but it may
  • be waiting for other resources from the operating system
  • such as processor.
    */


> Java Threaded**RUNNABLE**State actually includes threads of traditional operating system**ready**and**running**Two states.

## 4.2.3 BLOCKED

Blocking state. be in BLOCKED A thread in state is waiting for the lock to be released to enter the synchronization zone.

We use BLOCKED State as an example in life:

> If you are going to eat in the canteen after work today. You come to the only window in the canteen and find someone in front of the window. At this time, you have to wait for the person in front to leave from the window. Suppose you are a thread t2,The man in front of you is thread t1. here t1 Occupied the lock (the only window of the canteen), t2 Waiting for lock release, so at this time t2 Just in BLOCKED Status.

## 4.2.4 WAITING

Wait state. The waiting thread becomes RUNNABLE State requires other threads to wake up.

Calling the following three methods will make the thread enter the waiting state:

- 

 Object.wait(): Make the current thread wait until another thread wakes it up;

- 

 Thread.join(): Wait for the thread to execute, and the underlying call is Object Instance wait method;

- 

 LockSupport.park(): Disable the current thread for thread scheduling unless the call permission is obtained.  

Let's continue the above example and explain it WAITING Status:

> You waited for several minutes, and now it's your turn. Suddenly, one of your "ignorant" managers suddenly came. When you see him, you have a bad feeling. Sure enough, he came to you.
>
> He pulled you aside and told you to have dinner later. He said he would give a report this afternoon. He came to you to learn about the project. Although there are 10000 unwilling in your heart, you still walk away from the canteen window.
>
> At this point, suppose you are still a thread t2,Who is your manager t1. Although you have the lock (window) at this time, you still have to release the lock when the "uninvited guest" comes. At this time you t2 My state is WAITING. Then the manager t1 Get lock, enter RUNNABLE Status.
>
> If the manager t1 Don't take the initiative to wake you up t2(notify,notifyAll..),You can say t2 Can only wait.

## 4.2.5 TIMED_WAITING

Timeout wait state. The thread waits for a specific time, and will be awakened automatically when the time expires.

Calling the following method will cause the thread to enter the timeout waiting state:

- 

 Thread.sleep(long millis): Make the current thread sleep for a specified time;

- 

 Object.wait(long timeout): The thread sleeps for a specified time. The waiting period can be passed notify()/notifyAll()Wake up;

- 

 Thread.join(long millis): Wait for the current thread to execute at most millis Milliseconds, if millis If it is 0, it will be executed all the time;

- 

 LockSupport.parkNanos(long nanos):  Disable the current thread for thread scheduling for a specified time unless the call permission is obtained;

- 

 LockSupport.parkUntil(long deadline): The same as above, which also prohibits the thread from scheduling for a specified time;

Let's continue the above example to explain TIMED_WAITING Status:

> At noon the next day, it's dinner time again, and you still arrive at the window.
>
> Suddenly I remembered that your colleague asked you to wait for him. He said he asked you to wait for him for ten minutes. He changed his job bug. 
>
> Well, you said you would wait, and you left the window. Ten minutes passed quickly. You see he hasn't come yet. You think you've waited so long and haven't come yet. You'd better go to dinner first.
>
> At this time, you are still thread t1,You change bug My colleague is thread t2. t2 Give Way t1 Waiting for the specified time, t1 First actively released the lock. here t1 Waiting period belongs to TIMED_WATING Status.
>
> t1 After waiting for 10 minutes, you will wake up automatically and have the qualification to compete for the lock.

## 4.2.6 TERMINATED

Termination status. At this point, the thread has completed execution.

# 4.3 transition of thread state

According to the above description of thread status, we can get the following**Thread state transition diagram**:  [External chain picture transfer failed,The origin station may have anti-theft chain mechanism,It is recommended to save the picture and upload it directly(img-EJqsQiOf-1643883116959)(https://3803541485-files.gitbook.io/~/files/v0/b/gitbook-28427.appspot.com/o/assets%2F-L_5HvtIhTFW9TQlOF8e%2F-L_5TIKcBFHWPtY3OwUo%2F-L_5TJM7CvFzBq6T_50d%2F%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE.png?generation=1551665550335701&alt=media)]

## 4.3.1 transition between blocked and RUNNABLE States

We said above: in BLOCKED The status of the thread is because it is waiting for the release of the lock. Suppose there are two threads here a and b,a The thread has obtained the lock in advance and has not released the lock yet. At this time b Just in BLOCKED Status. Let's start with an example:

```java
@Test
public void blockedTest() {

   Thread a = new Thread(new Runnable() {
       @Override
       public void run() {
           testMethod();
       }
   }, "a");
   Thread b = new Thread(new Runnable() {
       @Override
       public void run() {
           testMethod();
       }
   }, "b");
   
   a.start();
   b.start();
   System.out.println(a.getName() + ":" + a.getState()); // Output?
   System.out.println(b.getName() + ":" + b.getState()); // Output?

}

// Synchronization method contention lock
private synchronized void testMethod() {
   try {
       Thread.sleep(2000L);
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
}

At first glance, you may think that thread a will call the synchronization method first, and thread is called in the synchronization method The sleep () method must output TIMED_WAITING, while thread b must output BLOCKED because it waits for thread a to release the lock.

In fact, there are two points worth noting. One is that there is a main thread in the test method blockedTest(), and the other is that it still takes some time to execute the run method after starting the thread. Without breaking points, RUNNABLE should be output in the above code.

The main thread of the test method only ensures that the two threads a and b call the start() method (converted to the RUNNABLE state). The status of the two threads (RUNNABLE) has been printed before the two threads really start competing for the lock.

At this time, you may ask again, what should I do if I want to print out the BLOCKED status? In fact, just deal with the main thread in the test method. You can let it "rest for a while", break the point or call thread Sleep method is OK.

What needs to be noted here is the rest time of the main thread. During the time when the thread competes for the lock, don't wait until the previous thread lock is released. You can't get the BLOCKED state at this time.

Let's change the above test method blockedTest():

`public void blockedTest() throws InterruptedException {`
    `······`
    `a.start();`
    `Thread.sleep(1000L); // Note that the main thread has been dormant for 1000 milliseconds, while the testMethod() has been dormant for 2000 milliseconds`
    `b.start();`
    `System.out.println(a.getName() + ":" + a.getState()); // Output`
    `System.out.println(b.getName() + ":" + b.getState()); // Output`
`}`

In this example, since the main thread is dormant, the run() method of thread a is executed, and thread b is executed.

After thread a executes run() and calls testMethod(), thread a sleeps for 2000ms (note that the lock is not released here), the main thread sleeps, and then thread b cannot compete for the lock when executing, so the output here is:

a:TIMED_WAITING

b:BLOCKED

4.3.2 transition between waiting state and RUNNABLE state

According to the transition diagram, we know that there are three methods to change the thread from the RUNNABLE state to the WAITING state. We mainly introduce object Wait() and thread join(). Object.wait()

Before calling the wait() method, the thread must hold the lock of the object.

When a thread calls the wait() method, it will release the current lock until another thread calls the notify()/notifyAll() method to wake up the thread waiting for the lock.

It should be noted that the other thread invokes the notify() method only to wake up a single thread waiting for a lock. If there are multiple threads waiting for this lock, it will not necessarily wake up the thread before calling the wait() method.

Similarly, after calling the notifyAll() method to wake up all threads waiting for the lock, it is not necessary to immediately allocate the time slice to the thread that just gave up the lock. The details depend on the system scheduling.

Thread.join()

Calling the join() method will not release the lock and will wait for the current thread to complete execution (transition to the TERMINATED state).

Let's change the above example where the thread starts:

public void blockedTest() {
    ······
    a.start();
    a.join();
    b.start();
    System.out.println(a.getName() + ":" + a.getState()); // Output TERMINATED
    System.out.println(b.getName() + ":" + b.getState());
}

If the join method is not called, the main thread will continue to go down regardless of whether thread a has finished executing or not.

The join method is called immediately after thread a starts. Here, the main thread will wait until thread a finishes executing, so the printing status of thread a is fixed as terminated.

As for the status of thread b, it is possible to print RUNNABLE (not yet entering the synchronization method) or TIMED_WAITING (entering the synchronization method).

4.3.3 TIMED_WAITING and RUNNABLE state transition

TIMED_ The status of WAITING is similar to that of WAITING, except that it is timed_ The WAITING state wait time is specified.

Thread.sleep(long)

Causes the current thread to sleep for a specified time. It should be noted that "sleep" here only temporarily stops the thread from executing and does not release the lock. When the time expires, the thread will re-enter the RUNNABLE state.

Object.wait(long)

The wait(long) method causes the thread to enter TIMED_WAITING status. The wait(long) method here is the same as the parameterless method wait(), which can be awakened by calling notify() or notifyAll() methods through other threads.

The difference is that even if other threads do not wake up with the parameter method wait(long), it will wake up automatically after the specified time long, and has the qualification to compete for the lock.

Thread.join(long)

join(long) causes the current thread to execute for a specified time and causes the thread to enter TIMED_WAITING status.

Let's change the example just now:

public void blockedTest() {
    ······
    a.start();
    a.join(1000L);
    b.start();
    System.out.println(a.getName() + ":" + a.getState()); // Output TIEMD_WAITING
    System.out.println(b.getName() + ":" + b.getState());
}

a.join(1000L) is called here. Because the execution time of the specific a thread is specified and the execution time is less than the sleep time of the a thread, the a thread status outputs TIMED_WAITING.

b the thread state is still not fixed (RUNNABLE or BLOCKED).

4.3.4 thread interrupt

In some cases, we need to interrupt the thread when we find that we don't need it to continue after the thread starts. At present, there is no safe and direct method to stop threads in Java, but Java provides a thread interrupt mechanism to deal with the situation that threads need to be interrupted.

Thread interrupt mechanism is a cooperative mechanism. It should be noted that the interrupt operation does not directly terminate a thread, but notifies the thread to be interrupted to handle it by itself.

Briefly introduce the following methods about Thread interruption provided in Thread class:

Thread.interrupt(): interrupt the thread. The interrupt thread here will not stop the thread immediately, but set the interrupt status of the thread to true (the default is false);

Thread.interrupted(): test whether the current thread is interrupted. The interrupt state of the thread is set to true, which means that the interrupt state of the thread will be changed to false for two consecutive calls;

Thread.isInterrupted(): test whether the current thread is interrupted. Different from the above method, calling this method does not affect the interrupt state of the thread.

In the thread interrupt mechanism, when other threads notify the thread that needs to be interrupted, the thread interrupt status is set to true, but the specific processing of the thread that needs to be interrupted depends entirely on the interrupted thread itself. You can actually process the interrupt request at the appropriate time, or continue to execute without processing at all.

reference material

JDK 1.8 source code

Drill down to thread sleep

The inevitable Java "lock"

Java thread state analysis

Relationship between Java threads and operating system threads

Detailed analysis of Java interrupt mechanism

5 communication between Java threads

Rational use of Java multithreading can make better use of server resources. Generally speaking, threads have their own private thread context and do not interfere with each other. However, when we need multiple threads to cooperate with each other, we need to master the communication mode of Java threads. This article will introduce several communication principles between Java threads.

5.1 lock and synchronization

In Java, the concept of lock is based on object, so we often call it object lock. The relationship between thread and lock can be understood by marriage. A lock can only be held by one thread at a time. In other words, if a lock is "married" (held) with a thread, if other threads need to get the lock, they have to wait for the thread to "divorce" (release) with the lock.

There is A concept of synchronization between our threads. What is synchronization? Suppose we have two students who are copying the answers of summer homework: thread A and thread B. When they were copying, the teacher suddenly came and revised some answers. Maybe the summer homework written by A and B was different. In order for A and B to write the same two summer homework, we need to ask the teacher to modify the answers first, and then students A and B copy them. Or students A and B copy first, and the teacher will modify the answer. This is the thread synchronization of thread A and thread B.

It can be interpreted as: thread synchronization refers to the execution of threads in a certain order.

In order to achieve thread synchronization, we can use locks to implement it.

Let's take a look at a lockless program:

public class NoneLock {

    static class ThreadA implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("Thread A " + i);
            }
        }
    }
    
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("Thread B " + i);
            }
        }
    }
    
    public static void main(String[] args) {
        new Thread(new ThreadA()).start();
        new Thread(new ThreadB()).start();
    }

}

Execute this program, and you will see on the console that thread A and thread B work independently and output their own print values. Here are the results of A run on my computer. The results will be different each time.

....
Thread A 48
Thread A 49
Thread B 0
Thread A 50
Thread B 1
Thread A 51
Thread A 52
....

Now I have A requirement. I want to wait for A to execute it first, and then B to execute it. What should I do? The simplest way is to use an "object lock":

public class ObjectLock {
    private static Object lock = new Object();

    static class ThreadA implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                for (int i = 0; i < 100; i++) {
                    System.out.println("Thread A " + i);
                }
            }
        }
    }
    
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                for (int i = 0; i < 100; i++) {
                    System.out.println("Thread B " + i);
                }
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        new Thread(new ThreadA()).start();
        Thread.sleep(10);
        new Thread(new ThreadB()).start();
    }

}

An object lock named lock is declared here. In the code blocks that need to be synchronized in ThreadA and ThreadB, we use the synchronized keyword to add the same object lock.

As mentioned above, according to the relationship between thread and lock, only one thread holds one lock at A time, so thread B will release the lock after thread A completes execution, and thread B can obtain the lock.

Here, the sleep method is used in the main thread for 10 milliseconds to prevent thread B from getting the lock first. Because if you start at the same time, thread A and thread B are in the ready state, the operating system may let B run first. In this way, the contents of B will be output first, and then the lock will be released automatically after the execution of B, and thread A will execute again.

5.2 waiting / notification mechanism

The above method is based on "lock". The thread needs to constantly try to obtain the lock. If it fails, continue to try again. This may consume server resources.

The waiting / notification mechanism is another way.

The waiting / notification mechanism of Java multithreading is implemented based on the wait() method and notify() notifyall() method of Object class.

The notify() method will randomly wake up a waiting thread, while notifyAll() will wake up all waiting threads.

As we mentioned earlier, A lock can only be held by one thread at A time. If thread A now holds A lock and starts executing, it can use lock Wait() puts itself into A waiting state. At this time, the lock is released.

At this time, thread B obtains the lock and starts to execute. It can use lock at A certain time Notify() notifies thread A that previously held the lock lock and entered the waiting state, saying "thread A, you don't have to wait, you can proceed".

It should be noted that thread B does not release the lock at this time, unless thread B uses lock at this time Wait() releases the lock, or thread B releases the lock after execution, and thread A can get the lock.

We use code to realize the following:

public class WaitAndNotify {
    private static Object lock = new Object();

    static class ThreadA implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println("ThreadA: " + i);
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.notify();
            }
        }
    }
    
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                for (int i = 0; i < 5; i++) {
                    try {
                        System.out.println("ThreadB: " + i);
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.notify();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        new Thread(new ThreadA()).start();
        Thread.sleep(1000);
        new Thread(new ThreadB()).start();
    }

}

// Output:
ThreadA: 0
ThreadB: 0
ThreadA: 1
ThreadB: 1
ThreadA: 2
ThreadB: 2
ThreadA: 3
ThreadB: 3
ThreadA: 4
ThreadB: 4

In this Demo, thread A and thread B first print out what they need, then use the notify() method to wake up another waiting thread, and then use the wait() method to fall into the wait and release the lock.

It should be noted that the wait / notification mechanism uses the same object lock. If your two threads use different object locks, they cannot communicate with each other using the wait / notification mechanism.

5.3 semaphore

JDK provides a Semaphore like function. But this article is not to introduce this class, but to introduce a Semaphore communication based on volatile keyword.

There will be a special chapter to introduce volatile keyword later. Here is just a brief introduction.

The volatile keyword can ensure the visibility of memory. If a variable is declared with the volatile keyword and the value of this variable is changed in one thread, other threads will immediately see the changed value.

For example, I now have A requirement. I want thread A to output 0, then thread B to output 1, and then thread A to output 2... And so on. How should I achieve it?

code:

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

    static class ThreadA implements Runnable {
        @Override
        public void run() {
            while (signal < 5) {
                if (signal % 2 == 0) {
                    System.out.println("threadA: " + signal);
                    synchronized (this) {
                        signal++;
                    }
                }
            }
        }
    }
    
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            while (signal < 5) {
                if (signal % 2 == 1) {
                    System.out.println("threadB: " + signal);
                    synchronized (this) {
                        signal = signal + 1;
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        new Thread(new ThreadA()).start();
        Thread.sleep(1000);
        new Thread(new ThreadB()).start();
    }

}

// Output:
threadA: 0
threadB: 1
threadA: 2
threadB: 3
threadA: 4

We can see that a volatile variable signal is used to realize the "semaphore" model. Note here that volatile variables require atomic operations. Signal + + is not an atomic operation, so we need to use synchronized to "lock" it.

This demonstration method is not necessarily efficient

Application scenario of semaphore:

If in a parking lot, the parking space is our public resource, the thread is like a vehicle, and the gatekeeper plays the role of "semaphore".

Because in this scenario, multiple threads (more than 2) need to cooperate with each other, it is not so convenient for us to use simple "lock" and "waiting for notification mechanism". Semaphores can be used at this time.

In fact, many multithreaded communication tool classes provided in JDK are based on semaphore model. We will introduce some common communication tool classes in the third article later.

5.4 piping

Pipeline is a communication mode based on "pipeline flow". JDK provides PipedWriter, PipedReader, PipedOutputStream and PipedInputStream. The first two are character based and the last two are byte stream based.

The example code here uses character based:

public class Pipe {
    static class ReaderThread implements Runnable {
        private PipedReader reader;

        public ReaderThread(PipedReader reader) {
            this.reader = reader;
        }
    
        @Override
        public void run() {
            System.out.println("this is reader");
            int receive = 0;
            try {
                while ((receive = reader.read()) != -1) {
                    System.out.print((char)receive);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    static class WriterThread implements Runnable {
    
        private PipedWriter writer;
    
        public WriterThread(PipedWriter writer) {
            this.writer = writer;
        }
    
        @Override
        public void run() {
            System.out.println("this is writer");
            int receive = 0;
            try {
                writer.write("test");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static void main(String[] args) throws IOException, InterruptedException {
        PipedWriter writer = new PipedWriter();
        PipedReader reader = new PipedReader();
        writer.connect(reader); // Note that you must connect here before you can communicate
    
        new Thread(new ReaderThread(reader)).start();
        Thread.sleep(1000);
        new Thread(new WriterThread(writer)).start();
    }

}

// Output:
this is reader
this is writer
test

We passed in PipedWrite and PipedReader objects through the constructor of the thread. You can briefly analyze the execution process of this sample code:

The thread ReaderThread starts executing,

Thread ReaderThread uses pipeline reader Read() enters "blocking",

The thread WriterThread starts executing,

Thread WriterThread uses writer Write ("test") writes a string to the pipeline,

Thread WriterThread uses writer Close() ends the pipeline writing and the execution is completed,

The thread ReaderThread receives the string output by the pipeline and prints it,

Thread ReaderThread execution completed.

Application scenario of pipeline communication:

This is easy to understand. Using pipes is mostly related to I/O flow. When one thread needs to send a message (such as string) or file to another thread first, we need to use pipeline communication.

5.5 other communication related

The above introduces some basic principles and methods of inter thread communication. In addition, there are some knowledge points related to thread communication, which are introduced here.

5.5.1 join method

The join() method is an instance method of the Thread class. Its function is to make the current Thread fall into the "waiting" state, and then continue to execute the current Thread after the execution of the Thread of the join is completed.

Sometimes, the main thread creates and starts a sub thread. If a large number of time-consuming operations need to be performed in the sub thread, the main thread will often end before the end of the sub thread.

If the main thread wants to wait for the execution of the child thread to obtain some processed data in the child thread, it needs to use the join method.

Example code:

public class Join {
    static class ThreadA implements Runnable {

        @Override
        public void run() {
            try {
                System.out.println("I'm a child thread. I'll sleep for one second first");
                Thread.sleep(1000);
                System.out.println("I'm a child thread. I slept for a second");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new ThreadA());
        thread.start();
        thread.join();
        System.out.println("If not join Method, I will be typed first, and it will be different if I add it");
    }

}

Note that the join() method has two overloaded methods, one is join(long) and the other is join(long, int).

In fact, through the source code, you will find that the wait(long) method is used at the bottom of the join() method and its overloaded method.

For the join(long, int), by looking at the source code (JDK 1.8), it is found that the bottom layer is not accurate to nanoseconds, but simply judges and processes the second parameter.

5.5.2 sleep method

The sleep method is a static method of the Thread class. Its function is to make the current Thread sleep for a period of time. It has two methods:

Thread.sleep(long)

Thread.sleep(long, int)

Similarly, looking at the source code (JDK 1.8), it is found that the second method seems to only deal with the second parameter, which is not accurate to nanoseconds. It is actually the first method called.

It should be emphasized here: * * sleep method will not release the current lock, while wait method will** This is also the most common multi-threaded interview question.

They also have these differences:

wait can specify the time or not; sleep must specify the time.

wait releases cpu resources and locks at the same time; sleep releases cpu resources but does not release locks, so it is prone to deadlock.

wait must be placed in the synchronization block or synchronization method, and sleep can be placed anywhere

5.5.3 ThreadLocal class

ThreadLocal is a local thread copy variable utility class. The interior is maintained by a weakly referenced Map. Its principle is not introduced in detail here, but its use. In the future, there will be a separate chapter to introduce the principle of ThreadLocal class.

Some friends call ThreadLocal thread local variable or thread local storage. Strictly speaking, ThreadLocal class does not belong to the communication between multiple threads, but allows each thread to have its own "independent" variables, which do not affect each other. It creates a copy for each thread, and each thread can access its own internal copy variables.

The most commonly used ThreadLocal classes are the set method and the get method. Example code:

public class ThreadLocalDemo {
    static class ThreadA implements Runnable {
        private ThreadLocal<String> threadLocal;

        public ThreadA(ThreadLocal<String> threadLocal) {
            this.threadLocal = threadLocal;
        }
    
        @Override
        public void run() {
            threadLocal.set("A");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadA Output:" + threadLocal.get());
        }
    
        static class ThreadB implements Runnable {
            private ThreadLocal<String> threadLocal;
    
            public ThreadB(ThreadLocal<String> threadLocal) {
                this.threadLocal = threadLocal;
            }
    
            @Override
            public void run() {
                threadLocal.set("B");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("ThreadB Output:" + threadLocal.get());
            }
        }
    
        public static void main(String[] args) {
            ThreadLocal<String> threadLocal = new ThreadLocal<>();
            new Thread(new ThreadA(threadLocal)).start();
            new Thread(new ThreadB(threadLocal)).start();
        }
    }

}

// Output:
ThreadA Output: A
ThreadB Output: B

You can see that although the two threads use the same ThreadLocal instance (passed in through the constructor), they can access a value of their current thread.

What does ThreadLocal do? If you just want thread isolation, declare a private variable in each thread. Why use ThreadLocal?

If developers want to associate a static variable (user ID or transaction ID) of the class with the thread state, they can consider using ThreadLocal.

The most common usage scenario of ThreadLocal is to solve database connection, Session management, etc. Database connection and Session management involve the initialization and closing of multiple complex objects. If you declare some private variables in each thread to operate, the thread will become less "lightweight" and need to create and close connections frequently.

5.5.4 InheritableThreadLocal

InheritableThreadLocal class is slightly different from ThreadLocal class. Inheritable means inheritance. Not only can the current thread access the copy value, but also its child threads can access the copy value.

reference material

JDK 1.8 source code

Deep understanding of thread communication

Communication mode between JAVA multithreaded threads

} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA output:" + threadLocal.get());
}

    static class ThreadB implements Runnable {
        private ThreadLocal<String> threadLocal;

        public ThreadB(ThreadLocal<String> threadLocal) {
            this.threadLocal = threadLocal;
        }

        @Override
        public void run() {
            threadLocal.set("B");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadB Output:" + threadLocal.get());
        }
    }

    public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        new Thread(new ThreadA(threadLocal)).start();
        new Thread(new ThreadB(threadLocal)).start();
    }
}

}

//Output:
ThreadA output: A
ThreadB output: B

As you can see, although the two threads use the same ThreadLocal Instances (passed in through the constructor), but they can each access a value of their current thread.

that ThreadLocal What does it do? If you just want thread isolation, declare a private variable in each thread. Why use it ThreadLocal?

If the developer wants to use a static variable of the class( user ID perhaps transaction ID)Associated with thread state, you can consider using ThreadLocal. 

Most common ThreadLocal The usage scenario is used to solve database connection Session Management, etc. Database connection and Session Management involves the initialization and shutdown of multiple complex objects. If you declare some private variables in each thread to operate, the thread will become less "lightweight" and need to create and close connections frequently.

## 5.5.4 InheritableThreadLocal

InheritableThreadLocal Class and ThreadLocal Class is slightly different, Inheritable It means inheritance. Not only can the current thread access the copy value, but also its child threads can access the copy value.

**reference material**

- 

  JDK 1.8 Source code

- 

  [Deep understanding of thread communication](http://ifeve.com / deep understanding of thread communication /)

- 

  [JAVA Communication mode between multithreaded threads](https://www.cnblogs.com/hapjin/p/5492619.html)

- 

  [Thread communication](http://ifeve.com/thread-signaling/)

Topics: Java Back-end Multithreading