Simple understanding of Java multithreading

The notes made when I first learned multi-threading may be a bit one-sided, but it is easy to read and understand, and it is easy for beginners to accept.

Multithreading

  • program

    A collection of instructions written in a language to accomplish a specific task. That is, a static code, a static object.

  • process

    An execution of a program, or a program that is running. It is a dynamic process: it has its own birth, existence and death. —-life cycle.

    A process is the unit of resource allocation, and the system allocates a different memory area for each process during runtime.

  • thread

    A process can be further refined into a thread, which is an execution path inside a program.

    If a process executes multiple threads in parallel at the same time, it supports multi-threading.

    Threads are the unit of scheduling and execution, and each thread has an independent running stack and program counter (pc).

Benefits

  1. Improve application responsiveness. Makes more sense for graphical interfaces and enhances user experience.
  2. Improve CPU utilization of computer system
  3. Improve program structure.

Parallelism and Concurrency

  • Parallelism: Multiple CPUs execute multiple tasks at the same time.
  • Concurrency: A CPU (using time slices) executes multiple tasks at the same time. (A CPU performs other tasks in the spare time of performing tasks. For example: cooking while steaming steamed buns)

Detailed explanation: https://blog.csdn.net/qq_44938023/article/details/124369919

Multi-thread creation (four creation methods)

Method 1_Inheriting the Thread class

Inherit the Thread class

  1. Create a subclass that inherits from the Thread class
  2. Rewrite the run() of the Thread class, and declare the operations performed by this thread in run()
  3. Create a subclass object of the Thread class
  4. Call start() on this object

start() function:

  1. start the current thread
  2. Call run() of the current thread

Common methods of Thread

  1. start(): Start the current thread; call the run() of the current thread
  2. run(): It is usually necessary to override this method in the Thread class, and declare the operations to be performed by the created thread in this method
  3. currentThread(): static method, returns the thread pointing to the current code
  4. getName(): Get the current thread name
  5. setName(): Set the current thread name
  6. yield(): Release the execution right of the current cpu.
  7. join(): Call the join() of thread b in thread a. At this time, thread a can enter the blocking state. Thread a will not end the blocking state until thread b is completely executed (It can be understood as jumping in the queue strong>, deprecated)
  8. sleep(long millis): Makes the current thread “sleep” for the specified milliseconds. During the specified time, the current thread is blocked
  9. isAlive(): Determine whether the current thread is alive
  10. getPriority(): Get thread priority
  11. setPriority(int xxx): Set the thread priority, set before the thread starts.

Method 2_ Implement the Runnable interface

  1. Create a class and implement the Runnable interface
  2. The implementation class implements the abstract method in Runnable: run()
  3. Create an object of the implementation class
  4. Pass this object as a parameter to the constructor of the Thread class to create an object of the Thread class
  5. Call start() through an object of the Thread class

The method of implementing the Runnable interface is preferred during development

reason:

  1. Implemented in a way that does not have the limitations of single inheritance of classes
  2. The way of implementation is more suitable to handle the situation where multiple threads have shared data (one object is used by multiple threads)

Add a new_implement Callable interface

Callable is more powerful than using Runnable

  • Compared with the run() method, it can have a return value
  • method can throw an exception
  • Support for generic return values
  • Need to use the FutureTask class

Future interface

  • Can cancel the execution results of specific Runnable and Callable tasks, query whether it is completed, obtain results, etc.
  • FutrueTask is the only implementation class of the Futrue interface
  • FutureTask also implements the Runnable and Future interfaces. It can be executed by a thread as a Runnable, and can also be used as a Future to get the return value of Callable

Add two_thread pool

Create multiple threads in advance, put them into the thread pool, obtain them directly when using them, and put them back into the pool after use. It can avoid frequent creation and destruction and realize reuse.

Creation of thread pool

//1. Provide a thread pool with a specified number of threads
ExecutorService service = Executors. newFixedThreadPool(10);

//2. Execute the operation of the specified thread. An object that implements the Runnable interface or the Callable interface implementation class needs to be provided
service.execute(new NumberThread()); //Applies to Runnable
service.submit(Callable callable); //Applies to Callable

//3. Close the connection pool
service. shutdown();

Benefits:

  1. Improved impact speed (reduced time to create new threads)

  2. Reduce resource consumption (reuse threads in the thread pool, do not need to create each time)

  3. Easy thread management:

    • corePoolSize: the size of the core pool
    • maximumPoolSize: maximum number of threads
    • keepAliveTime: When the thread has no tasks, how long does it keep at most before terminating

Thread lifecycle

  • **New:** When an object of the Thread class or its subclasses is declared and created, the new thread object is in the new state
  • **Ready: **After the thread in the newly created state is started(), it will enter the thread queue and wait for the CPU time slice. At this time, it has the conditions to run, but it has not been allocated CPU resources.
  • **Run:** When a ready thread is scheduled and obtains CPU resources, it enters the running state. The run() method defines the operation and function of the thread
  • **Blocking:** Under certain special needs, when it is artificially suspended or performs input and output operations, it will give up the CPU and temporarily suspend its own execution, and enter the blocking state
  • **Death:** The thread has completed all its work or the thread was forcibly terminated early or an exception caused the end

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-wjpyUCAX-1679393611898) (E:\project\\
otes\JAVA\picture\java advanced\ \Thread Status.png)]

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-8ilsv5kD-1679393611899) (E:\project\\
ote\JAVA\picture\java advanced\ \Thread state 2.png)]

Thread stop (stop)

It is not recommended to use the stop() and destroy() methods provided by JDK. (Deprecated)

It is recommended that the thread stop itself

It is recommended to use a flag bit to terminate the variable. When flag=false, the thread will be terminated.

public class TestStop implements Runnable{<!-- -->
    //1. Set a logo
    private boolean flag = true;

    @Override
    public void run() {<!-- -->
        //2. The thread body uses this logo
        while(flag){<!-- -->
            System.out.println("run... Thread");
        }
    }

    //3. Externally provide methods to change the logo
    public void stop(){<!-- -->
        this.flag = false;
    }
}

Thread sleep (sleep)

  • sleep specifies the number of milliseconds that the current thread blocks;
  • sleep has an exception InterruptedException;
  • After the sleep time is up, the thread enters the ready state;
  • sleep can simulate network delay, countdown, etc.;
  • Each object has a lock, sleep will not release the lock.

Puts the current thread to “sleep” for the specified milliseconds. During the specified time, the current thread is blocked

Thread yield (yield)

When thread A enters the CPU to run, it bows to thread B, and the two threads come out again to wait for the CPU to execute. Courtesy may not be successful, and the choice of the CPU depends on the mood of the CPU.

Thread enforcement (join)

Call the join() of thread b in thread a. At this time, thread a can enter the blocking state. Thread a will not end the blocking state until thread b is completely executed (It can be understood as jumping in the queue, not recommended)

Thread priority

Thread priority is represented by numbers, ranging from 1 to 10. Three class constants are declared in the Thread class:

  • THERAD.MIN_PRIORITY=1; (minimum)
  • THERAD.MAX_PRIORITY=10; (highest)
  • THERAD.NORM_PRIORITY=5; (default, such as the main method)

A low priority just means that the probability of getting scheduled becomes lower. It still depends on the scheduling of the CPU. It does not necessarily mean that the high priority will run first. It will cause performance inversion, and the one with higher priority will be executed later.

Thread guard

  • Threads are divided into user threads and daemon threads
  • The virtual machine must ensure that user threads are executed to completion
  • The virtual machine does not have to wait for the daemon thread to finish executing
  • Such as: recording operation logs in the background, monitoring memory, waiting for garbage collection…

Thread lock

Application scenarios and principles of mutex locks, spin locks, read-write locks, pessimistic locks, and optimistic locks

Thread synchronization (solve concurrency)

When dealing with multi-threading issues, multiple threads access the same object, and some threads want to modify this object, then we need thread synchronization. Thread synchronization is actually a waiting mechanism. Multiple threads that need to access this object at the same time enter the object waiting pool to form a queue, and wait for the previous thread to be used before the next thread can use it. (understand as queuing)

synchronized

Since multiple threads of the same process share the same storage space, while bringing convenience, it also brings access conflicts. In order to ensure the correctness of data when accessed in the method, a lock mechanism is added when accessing synchronized. When a thread acquires an exclusive lock on an object and monopolizes resources, other threads must wait and release the lock after use. There are the following problems:

  • A thread holding a lock causes all other threads that need the lock to hang;
  • Under multi-thread competition, locking and releasing locks will cause more context switching and scheduling delays, causing performance problems;
  • If a high-priority thread waits for a low-priority thread to release the lock, it will cause priority inversion and cause performance problems. (It only takes a few seconds for me to go to the restaurant to buy sausage, but I have to wait in line for more than ten minutes. It is morally correct, but it affects the efficiency)

Synchronized code blocks

synchronized (synchronized monitor) {<!-- -->
    //Code that needs to be synchronized
}

Description:

  1. The code that operates the shared data is the code that needs to be synchronized
  2. Shared data: Variables that multiple threads operate on together.
  3. Synchronization monitor, commonly known as: lock. Objects of any class can act as locks.
  • Multiple threads must share a lock.

  • In the way of implementing the Runnable interface to create multiple threads, you can consider using this as a synchronization monitor.

Sync method

Encapsulate the code for sharing data into a method, and declare this method as a synchronous method.

Summary:

  1. Synchronous methods still involve a sync monitor, they just don’t need the declaration we show

  2. For non-static synchronous methods, the synchronous monitor is: this

    The static synchronization method: the synchronization monitor is: the current class itself

Pros and Cons

Advantages: Solve the problem of thread safety

Disadvantage: When operating synchronous code, only one thread can participate, while other threads wait. It is equivalent to a single-threaded process with low efficiency.

Lock

New in JDK5.0

Lock needs to manually start the synchronization lock(), and at the same time end the synchronization also needs to manually implement unlock()

// 1. Instantiate ReentrantLock
private ReentrantLock lock = new ReentrantLock();
// 2. Use try{} to wrap the code block;
try{<!-- -->
    // call lock()
    lock. lock();
}finally{<!-- -->
    // 3. Call the unlock method: unlock()
    lock. unlock();
}

The difference between the two

  1. Lock is an explicit lock (manually open and close the lock), synchronized is an implicit lock, which is automatically released outside its scope
  2. Lock only has code locks, synchronized has code locks and method locks
  3. Lock lock, the JVM spends less time scheduling threads and has better performance. And has better extensibility (provide more subclasses)

Priority:

Lock → synchronized code block (in method body) → synchronized method (in method body)

Deadlock

Overview:

  • Different threads occupy the synchronization resources needed by the other party and do not give up. They are all waiting for the other party to give up the synchronization resources they need, which forms a thread deadlock.
  • After a deadlock occurs, there will be no exception and no prompt, but all threads are blocked and cannot continue

solve:

  • Minimize the definition of synchronization resources
  • Try to avoid nested synchronization

Thread communication

The interaction between threads is called thread communication.

Three methods involved:

  • wait(): Once executed, the current thread enters a blocked state and releases the synchronization monitor (release lock)
  • notify(): Once executed, wake up a thread that was waited on. If multiple threads are waited, wake up the thread with higher priority
  • notifyAll(): Once executed, wake up all waited threads

Instructions for the above three methods:

  1. Must be used in a synchronized code block or a synchronized method
  2. The caller must be a synchronous code block or a synchronous monitor in a synchronous method, otherwise an exception will occur
  3. All three methods are defined in the java.langObject class