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
- Improve application responsiveness. Makes more sense for graphical interfaces and enhances user experience.
- Improve CPU utilization of computer system
- 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
- Create a subclass that inherits from the Thread class
- Rewrite the run() of the Thread class, and declare the operations performed by this thread in run()
- Create a subclass object of the Thread class
- Call start() on this object
start() function:
- start the current thread
- Call run() of the current thread
Common methods of Thread
- start(): Start the current thread; call the run() of the current thread
- 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
- currentThread(): static method, returns the thread pointing to the current code
- getName(): Get the current thread name
- setName(): Set the current thread name
- yield(): Release the execution right of the current cpu.
- 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)
- sleep(long millis): Makes the current thread “sleep” for the specified milliseconds. During the specified time, the current thread is blocked
- isAlive(): Determine whether the current thread is alive
- getPriority(): Get thread priority
- setPriority(int xxx): Set the thread priority, set before the thread starts.
Method 2_ Implement the Runnable interface
- Create a class and implement the Runnable interface
- The implementation class implements the abstract method in Runnable: run()
- Create an object of the implementation class
- Pass this object as a parameter to the constructor of the Thread class to create an object of the Thread class
- Call start() through an object of the Thread class
The method of implementing the Runnable interface is preferred during development
reason:
- Implemented in a way that does not have the limitations of single inheritance of classes
- 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:
-
Improved impact speed (reduced time to create new threads)
-
Reduce resource consumption (reuse threads in the thread pool, do not need to create each time)
-
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:
- The code that operates the shared data is the code that needs to be synchronized
- Shared data: Variables that multiple threads operate on together.
- 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:
-
Synchronous methods still involve a sync monitor, they just don’t need the declaration we show
-
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
- Lock is an explicit lock (manually open and close the lock), synchronized is an implicit lock, which is automatically released outside its scope
- Lock only has code locks, synchronized has code locks and method locks
- 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:
- Must be used in a synchronized code block or a synchronized method
- The caller must be a synchronous code block or a synchronous monitor in a synchronous method, otherwise an exception will occur
- All three methods are defined in the java.langObject class