1. Basic concepts of multithreading
1.1 Processes and Threads
A program is a set of instructions written in a language to accomplish a specific task, that is, a static code, a static object.
Process: is a program execution process, or a running program, is a dynamic process, each program has an independent memory space
Thread: An execution path in a process that shares one memory space and can be switched between threads to execute concurrently. A process must have at least one thread
Thread is actually a further division on the basis of the process. After a process is started, several execution paths in it can be divided into several threads
1.2 Parallelism and Concurrency
Concurrency: Two or more events occur in the same time period.
Parallelism: Two or more events occurring at the same time (simultaneously).
1.3 Synchronous and Asynchronous
Synchronization: Queue execution, inefficient but safe.
Asynchronous: Simultaneous execution is efficient but data is insecure.
1.4 Thread scheduling
Time-sharing scheduling (time slice) : all threads take turns to use the CPU and allocate the CPU time equally for each thread. Preemptive scheduling: high-priority threads seize the CPU
Java uses preemptive scheduling.
The CPU uses preemptive scheduling to switch between threads at high speed. For a CPU core, only one thread can execute at a time, and the CPU switches between threads much faster than we might think, seemingly running at the same time. In fact, multi-threaded programs can not improve the speed of the program, but can improve the efficiency of the program, so that the CPU usage is higher.
1.5 Thread Priority
Java threads have priority, and threads with higher priority get more opportunities to run.
Java Thread priorities are integers ranging from 1 to 10. The Thread class has the following three static constants:
Static int MAX_PRIORITY Specifies the highest priority that a thread can have. The value is 10. Static int MIN_PRIORITY Minimum priority that a thread can have. The value is 1. Static int NORM_PRIORITY Specifies the default priority assigned to a thread. The value is 5.
The setPriority() and getPriority() methods of the Thread class set and get the priority of the Thread, respectively. The default priority of the main Thread is thread.norm_priority. SetPriority (int newPriority): changes the priority of a thread. But on a probabilistic basis, threads with higher priority are more likely to be executed. This does not mean that low-priority threads execute only after high-priority threads finish executing.
2. Three ways to create multiple threads
2.1 inherits from Thread
1. Create a subclass that integrates with Thread (search for the run method by pressing CTRL + O (override)) 2. Overrides the run () method of Thread class 3. Creates object 4 of Thread subclass. Call the start () method from this object
Public class MyThread extends Thread{public class MyThread extends Thread{public class MyThread extends Threadrun() {// The code here is a new execution path // the execution path is triggered by the start () method of the Thread object instead of calling the run methodfor(int i=0; i<10; i++){ System.out.println("Big big"+i);
}
}
}
public static void main(String[] args) {
MyThread m = new MyThread();
m.start();
for(int i=0; i<10; i++){ System.out.println("Little Star"+i);
}
Copy the code
2.2 Implementing the Runable Interface Mode
Create a class that implements the Runable interface. 2. Implement the abstract Runnable method: run() 3. Create an object of the implementation class. 4. Pass this object as a parameter to the constructor of Thread, creating an object of Thread 5. Call start () from an object of the Thread class
public class MyRunnable implements Runnable{
@Override
public void run() {// Task of threadfor(int i=0; i<10; i++){ System.out.println("Moonlight by my bed."+i); MyRunnable r = new MyRunnable(); T = new Thread(r); t = new Thread(r); //3. Start the thread t.start();for(int i=0; i<10; i++){ System.out.println("I think it's frost on the ground."+i);
Copy the code
Implementing Runnable has the following advantages over inheriting Thread
1. To achieve multi-threading by creating tasks and assigning them to threads, it is more suitable for multiple threads to perform the same task at the same time. 2. The limitations of single inheritance can be avoided. 3. Tasks are separated from threads themselves, which improves the robustness of programs. The Thread pool technique, which we will learn later, accepts tasks from the Runnable interface rather than threads of the Thread type
The main method is actually a thread. In Java, all threads are started at the same time, and when and which are executed first depends on who gets the CPU resources first. In Java, at least two threads are started each time a program runs. One is the main thread and the other is the garbage collector thread. Because every time a class is executed using a Java command, a JVM is actually started, and each JVM is actually starting a process in the operating system.
2.3 Implement the Callable interface mode
1. Create an implementation class that implements callable. 2. Implement the Call method and declare the operations that this thread needs to perform in call () 3. Create an object of the Callable implementation class 4. Pass the object of the Callable interface implementation class to the FutureTask constructor to create an object of FutureTask 5. Pass the FutureTask object as an argument to the Thread constructor, create the Thread object, and start it by calling the start method (get gets the return value of call from the Thread via the FutureTask object call method)
//Callable Interface Public interface Callable<V> {V call() throws Exception; {@override public <T> Call () throws Exception {class XXX implements Callable<T> {@override public <T> Call () throws Exception {returnT; FutureTask<Integer> Future = new FutureTask<>(Callable); New Thread(future).start();Copy the code
Similarities and differences between Runnable and Callable
Common: Both interfaces can write multithreaded programs. Both use thread.start () to start threads
Difference: Runnable has no return value; Call () of the Callable interface allows an exception to be thrown; Runnable run() cannot be thrown
Callable also gets the return value — the Callalble interface supports returning the result of the execution, which can be obtained by calling futureTask.get (). This method blocks the main process’s execution and does not block if not called.
3. Thread safety issues
Thread safety refers to that when multiple threads operate on the same shared data, the thread does not have time to update the shared data. As a result, other threads do not get the latest data, resulting in thread safety problems.
Three types of security locks:
3.1 Synchronizing code blocks
Synchronized {// Code that needs to be Synchronized}
Description:
Code that operates on shared data (code that operates on data shared by all threads) (seen as a toilet area (toilet shared by all)), that is, code that needs to be shared (synchronized code block, in synchronized code block, which is equivalent to a single thread, inefficient) : Data operated by multiple threads, such as a public toilet, is analogous to a shared data synchronization monitor (commonly known as a lock) : any object can act as a lock. When locked, only one thread can enter (requirement: multiple threads must share the same lock, such as the toilet on a train, the same sign indicates that there are people).
3.2 Synchronization Method
Use a synchronized method to modify the method with the synchronized keyword. Extract the synchronized code block as a method, which is decorated with the synchronized keyword. For the runnable interface to implement multithreading, you only need to use the synchronized method modifier. For inheriting from Thread, you need to use the static and synchronized methods modifier, because objects are not unique.
3.3 show the lock
The Lock subclass already
3.4 Fair lock and Unfair Lock
Displaying a lock with the fair parameter true indicates a fair lock is first come, first served
Public static void main(String[] args) {// Thread unsafe // synchronized code blocks and synchronized methods are implicit locks // solution 3. ReentrantLock Runnable Run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ private int count = 10; // Show lock l: fair parameter istruePrivate Lock l = new ReentrantLock(true);
@Override
public void run() {
while (true) { l.lock(); / / lockif (count > 0) {
System.out.println("Preparing to sell tickets."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(thread.currentThread ().getName() +"Ticket issued. Remaining tickets." + count);
}else {
break; } l.unlock(); // Unlock}}}Copy the code
3.5 Deadlocks Occur
After a deadlock occurs, there is no prompt, but all threads are blocked and cannot continue
Deadlock solutions:
1. Reduce the synchronization of shared variables. 2. Reduce lock nesting.
4. Thread communication problems
Common communication methods:
These three methods can only be used in synchronized code blocks or synchronized methods.
Application of thread communication: Producer/consumer Problem 1. Is it multithreading problem? Yes, there are producer threads and consumer threads (four ways to create multiple threads) 2. Is there a shared data problem with multithreading? There are shared data —- products (synchronization method, synchronization code block, lock lock) 3. Does multithreading have thread safety issues? All operations on shared data products exist at —-. (3 methods) 4. Whether there is communication between threads, yes, if the production exceeds 20, the production should be stopped (wait). (Communication between threads, wait, notify, etc.)
5. Thread life cycle
Phase description of the thread life cycle
New When an object of Thread or a subclass of Thread is declared and created, the new Thread object is in the new state. When the Thread in the new state is started, it enters the Thread queue and waits for the CPU time slice. The run method defines the actions and functions of a thread that blocks. In some special case, when it is artificially suspended or performing input/output operations, it gives up the CPU and temporarily terminates its execution. A dead thread completes all its work or is forced to terminate prematurely or an exception occurs causing the thread to terminate
6. Thread pool ExecutorService
6.1 Caching thread pools
/** * Cache thread pool. * (length unlimited) * execution process: * 1. Whether the thread pool is the idle thread * 2. There is use * 3. Doesn't exist, create a thread And in the thread pool, and then use the * / ExecutorService service = Executors. NewCachedThreadPool (); // Add a new task service. Execute (new) to the thread poolRunnable() {
@Override
public void run() {
System.out.println("Thread name :"+Thread.currentThread().getName()); }}); service.execute(newRunnable() {
@Override
public void run() {
System.out.println("Thread name :"+Thread.currentThread().getName()); }}); service.execute(newRunnable() {
@Override
public void run() {
System.out.println("Thread name :"+Thread.currentThread().getName()); }});Copy the code
6.2 Fixed-length thread pool
/** * fixed length thread pool. * (length is specified number) * execution flow: * 1. If there are no free threads and the thread pool is full, create a thread and add it to the thread pool. Then use * 4. There is no idle thread and thread pool is full of cases, the waiting thread pool is the idle thread * / ExecutorService service = Executors. NewFixedThreadPool (2); service.execute(newRunnable() {
@Override
public void run() {
System.out.println("Thread name :"+Thread.currentThread().getName()); }}); service.execute(newRunnable() {
@Override
public void run() {
System.out.println("Thread name :"+Thread.currentThread().getName()); }});Copy the code
6.3 Single-threaded thread pool
/** * single thread pool. Check whether the thread in the thread pool is free * 2. Not free, then wait for a single thread in the pool is used after free * / ExecutorService service = Executors. NewSingleThreadExecutor (); service.execute(newRunnable() {
@Override
public void run() {
System.out.println("Thread name :"+Thread.currentThread().getName()); }}); service.execute(newRunnable() {
@Override
public void run() {
System.out.println("Thread name :"+Thread.currentThread().getName()); }});Copy the code
6.4 Periodic task fixed-length thread pool
Public static void main(String[] args) {/** * Specifies the thread pool for periodic tasks. If there are no free threads and the thread pool is full, create a thread and add it to the thread pool. Then use * 4. If there are no free threads and the thread pool is full, wait for the thread pool to have free threads * * periodic task execution: * perform regularly, when a certain time to trigger, automatically perform a certain task. * / ScheduledExecutorService service = Executors. NewScheduledThreadPool (2); /** * Scheduled execution of * parameter 1. Runnable task * parameter 2. Duration number * Parameter 3. Unit of duration number */ /*service.schedule(newRunnable() {
@Override
public void run() {
System.out.println("Hey, hey, hey, hey!"); } },5,TimeUnit.SECONDS); */ /** * Periodic execution * Parameter 1. Runnable task * parameter 2. Duration number (latency for execution) * Parameter 3. Cycle duration (interval for each execution) * Parameter 4. Length of digital unit * / service. The scheduleAtFixedRate (newRunnable() {
@Override
public void run() {
System.out.println("Hey, hey, hey, hey!"); }}, 5, 2, TimeUnit. SECONDS); }Copy the code
7. Lambda expressions
Lambda embodies the idea of functional programming
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hhh"); }}); t.start(); Thread t = new Thread(() -> { System.out.println("hhh");
});
t.start();
Copy the code
This expression simply omits the intermediate interface function and replaces it with an expression, preserving the argument and method parts.
8. A small summary
The purpose of thread synchronization is to protect a resource from being damaged by multiple threads questioning it. Thread synchronization method is realized through the lock, cut each object has only one lock, the lock associated with a specific object, thread once get the object lock, other threads to access the object were unable to access the object other than synchronized methods For static synchronous method, the lock is in this Class, the lock object is the object of the Class. Static and non-static methods do not interfere with each other. A thread acquires the locks, which are acquired when a synchronized method on another object is accessed in a synchronized method. For synchronization, being aware of which object to synchronize on is key. To write thread-safe classes, we need to make correct judgment on the logic and safety of multiple threads competing for access to resources, analyze the “atomic” operation, and ensure that other threads cannot access the competing resources during the atomic operation. When multiple threads wait for an object lock, the thread that did not acquire the lock will block. Deadlocks are caused by threads waiting for each other to lock, and the probability of occurrence is very small in practice. Really let you write a deadlock program, may not work, ha ha. However, if a program deadlocks, it will die.