1. Overview of threads

1.1 Concepts related to threads

1.1.1 process
  • Process (Process) is a computer program about a data set on a running activity, is the operating system for resource allocation and scheduling of the basic unit.
  • A process can simply be thought of as a program running in the operating system.
1.1.2 thread
  • Thread (ThreadIs an execution unit of a process.
  • A thread is a single sequential flow of control in a process, an execution branch of the process
  • A process is a container for threads. A process has at least one thread, and a process can have multiple threads
  • In the operating system, resources are allocated on a process basis, such as virtual storage space, file descriptors, etc. Each thread has its own thread stack, its own register environment, and its own thread local storage
1.1.3 Main thread and Child Thread
  • When the JVM starts, it creates a main thread that executes main, which is the thread that runs main
  • Threads in Java are not isolated; there are some relationships between threads. If thread B is created in thread A, thread B is called A child thread of thread A, and the corresponding THREAD A is the parent thread of thread B
1.1.4 Serial, concurrent and parallel

  • Concurrency can improve the efficiency of processing, that is, more things can be processed or completed in a period of time
  • Parallelism is a more rigorous, ideal concurrency
  • From a hardware point of view, if a single-core CPU can execute only one thread at a time, the processor can use time-slice rotation technology, which allows the CPU to switch between threads quickly. To the user, it feels like three threads are running at the same time. If you have a multi-core CPU, you can allocate different CPU cores for different threads

1.2 Creating and starting threads

  • In Java, creating a thread is creating oneThreadThe object of the class
  • ThreadThere are two common class constructors:ThreadwithThread(Runnable), corresponding to the two ways to create a thread:
    • Define a subclass of Thread
    • Defines an implementation class for the Runnable interface
    • There is no essential difference between the two ways of creating threads
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");

        // create a child thread object
        MyThread thread = new MyThread();
        
        // (4) Start thread
        thread.start();
        
        System.out.println("Other code after the main thread..."); }}// (1) define the class to inherit Thread
class MyThread extends Thread {
    // (2) override the run() method body in the Thread parent class. The code in the run() method body is the task to be performed by the child Thread
    @Override
    public void run(a) {
        super.run();
        System.out.println("This is what the child thread prints."); }}Copy the code

The thread’s start() method is called to start the thread, which essentially asks the JVM to run the corresponding thread at a time determined by the thread Scheduler.

The end of the start() method call does not mean that the child thread starts running

The newly opened thread executes the run() method

If multiple threads are started, the order in which the start() calls are made is not necessarily the order in which the threads are started. The result of multithreading is independent of the order in which the code is executed or called

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");

        // 3) Create an implementation object for the Runnable interface
        MyRunnable runnable = new MyRunnable();

        //4) Create thread object
        Thread thread = new Thread(runnable);

        //5) Start the thread
        thread.start();

        // The current thread is main
        for (int i = 1; i <= 1000; i++) {
            System.out.println("main==> " + i);
        }

        The Thread(Runnable) constructor is sometimes called, and the argument is also passed an anonymous inner class object
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run(a) {
                for (int i = 1; i <= 1000; i++) {
                    System.out.println("sub ------------------------------> "+ i); }}}); thread2.start(); }}// 1) Define a class that implements the Runnable interface
class MyRunnable implements Runnable {
    // 2) Override the abstract run() method in the Runnable interface, which is the code to be executed by the child thread
    @Override
    public void run(a) {
        for (int i = 1; i <= 1000; i++) {
            System.out.println("sub thread --> "+ i); }}}Copy the code

When a Thread class already has a parent class, threads cannot be created by inheriting the Thread class. Instead, they can be created by implementing the Runnable interface

1.3 Common methods of threads

1.3.1 currentThread ()
  • Thread.currenThread()Method to get the current thread
  • Any piece of code in Java is executed in a thread, and the thread executing the current code is the current thread
  • The same piece of code may be executed by different threads, so the current thread is relative,Thread.currentThread()The return value of the method is the thread object where the code is actually running
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Main () prints the name of the current thread:" + Thread.currentThread().getName());

        // Create a child thread, call the MyThread() constructor, call the constructor in the main thread, so the current constructor thread is the main thread
        MyThread t1 = new MyThread();

        // Start the child Thread, which calls the run() method, so the current Thread in the run() method is the thread-0 child Thread
        t1.start();

        // Call the run() method directly from the main method, no new thread is opened, so the current thread in the run method is the main threadt1.run(); }}class MyThread extends Thread {
    public MyThread(a) {
        System.out.println("Constructor prints the name of the current thread:" + Thread.currentThread().getName());
    }

    @Override
    public void run(a) {
        System.out.println(The "run() method prints of course the money thread name:"+ Thread.currentThread().getName()); }}Copy the code
1.3.2 elegantly-named setName ()/getName ()
  • Thread.setname (" thread name "), set the thread name
  • thread.getName(), returns the thread name
  • Setting a thread name facilitates program debugging and improves the readability of the program. You are advised to set a name for each thread to reflect the functions of the thread
1.3.3 isAlive ()
  • thread.isAlive()Checks whether the current thread is active
  • The active state is when the thread is started and not terminated

1.4 Thread life cycle

  • The life cycle of a thread is the birth and death of a thread object, the state of the thread
  • The thread life cycle can passgetState()Method, the thread state isThread.StateDefined by an enumeration type, which has the following components:
1.4.1 NEW

New state. Creates the thread object’s state prior to the call to start()

1.4.2 RUNNABLE
  • Runnable state, which is a composite state containingREADYRUNNINGTwo states
  • READYState The thread can be scheduled by the thread scheduler so that it is inRUNNINGState,RUNNINGStatus indicates that the thread is executing
  • Thread.yield()Method can be used to separate threads fromRUNNINGState transition toREADYstate
1.4.3 BLOCKED

Blocked status. When a thread initiates a BLOCKED I/O operation or requests an exclusive resource occupied by another thread, the thread becomes BLOCKED. A blocked thread does not consume CPU resources. When the blocked I/O operation is complete, or the thread has acquired the resource requested by it, the thread can switch to the RUNNABLE state.

1.4.4 WAITING

Wait state. Wait (), thread.join(), object.notify(), or RUNNABLE after the added thread completes execution

1.4.5 TIMED_WAITING

A WAITING state is similar to a WAITING state. The difference is that a thread in this state does not wait indefinitely. If the thread does not complete the desired operation within the specified time range, the thread is automatically converted to RUNNABLE

1.4.6 TERMINATED

Terminating state, where a thread terminates

1.5 Advantages and Risks of multithreaded programming

1.5.1 Advantages of multithreaded programming
  • Improve system throughput: Multithreaded programming allows a process to have multiple concurrent operations
  • Improved responsiveness: The Web server uses dedicated threads to handle user requests, reducing user wait times
  • Make full use of multi-core processor resources: Use multi-threading to make full use of CPU resources
1.5.2 Risks of multithreaded programming
  • Thread safety issues: When data is shared by multiple threads, data consistency issues such as reading dirty data (stale data), missing data updates, and so on, can occur if the correct concurrent access control measures are not in place
  • Thread activity problem: a thread that remains in a non-runnable state due to defects in the program itself or due to resource scarcity is a thread activity problem. Common thread activity faults include the following: Deadlock, Lockout, Livelock, Starvation
  • Context switch: The processor switches from executing one thread to executing another
  • Reliability: One thread may cause the JVM to terminate unexpectedly, and other threads may fail to execute

2. Thread safety issues

Non-thread-safe means that when multiple threads operate on the instance variable of the same object, the value will be changed and the value will not be synchronized.

There are three aspects of thread safety: atomicity, visibility and order

2.1 atomic

Atoms just mean indivisible. The indivisibility of atomic operations has two implications:

  • An operation that accesses (reads, writes) a shared variable has either completed or has not yet occurred, as seen from other threads
  • Atomic operations that access the same set of shared variables cannot be interleaved

Java implements atomicity in two ways: either by using locks or by using the CAS instruction of the processor

Locks are exclusive, ensuring that shared variables can only be accessed by one thread at a time

CAS instructions are implemented directly at the hardware level and can be viewed as hardware locks

2.2 the visibility

In a multithreaded environment, when one thread updates a shared variable, subsequent threads may not be able to monitor the results of the update immediately. This is another form of thread-safety problem: visibility.

If a thread updates a shared variable, subsequent threads accessing the variable can read the update result, and the update result is visible to other threads. Otherwise, the update result is invisible to other threads

Multithreaded programs can cause other threads to read old data due to visibility problems

2.3 order

Orderliness refers to the conditions under which memory access operations performed by one thread running on one processor are seen as Out of Order by other threads running on another processor.

Out-of-order is when the order of memory access operations appears to change

2.3.1 reorder

In a multi-core processor environment, the sequential structure of code written, the order in which such operations are performed may not be guaranteed:

  • The compiler may change the order of the two operations
  • The processor may also not execute in the order of the object code

Reordering is a phenomenon in which multiple operations performed on one processor may appear to other processors in a different order than the order specified by the object code

Reordering is an optimization of ordered memory access operations that can improve the performance of a single-threaded program without affecting its correctness. However, it may affect the correctness of multithreaded programs, that is, it may lead to thread safety problems

Reordering, like visibility problems, is not inevitable

Several concepts related to the order of memory operations:

  • Source code order: is the memory access order specified in the source code
  • Program order: The order of memory access specified by the object code running on the processor
  • Execution order: The actual order in which memory access operations are executed on the processor
  • Perceived order: The perceived order of the memory access operations of this processor and other processors by a given processor

Reordering can be divided into instruction reordering and storage subsystem reordering:

  • Instruction reordering is usually caused by the JIT compiler or processor and is inconsistent between program order and execution order
  • Storage subsystem reordering is caused by caching, write buffers, and perception order is inconsistent with execution order
2.3.2 Command reordering

Instruction reordering occurs when the source order is inconsistent with the program order, or when the program order is inconsistent with the execution order. Instruction reordering is an action that does make adjustments to the order of instructions.

A Javac compiler generally does not perform instruction reordering, whereas a JIT compiler might. The processor may also perform instruction reordering, making the execution order inconsistent with the program order.

Instruction rearrangement does not affect the correctness of the results of single-threaded programs and may lead to unexpected results of multithreaded programs.

2.3.3 Storage Subsystem Reordering