Multithreading
Main idea: guide step by step in multiple scenarios to understand and learn Java threads from "why, what and how"
Scenario 1: getting to know the thread concept
When using QQ, you can send messages to each other and receive messages from others
Why do you need the concept of threads?
- Personal understanding:
If you follow the idea of writing a main method in the past, after writing the methods of sending and receiving messages respectively, you have to call the two methods circularly at a certain time interval, which is more troublesome, and you can't realize simultaneous and parallel.
However, when the concept of thread is introduced, the methods of sending and receiving messages can be separated:
Thread 1 waits for itself to enter a message and send a message,
Thread 2 waits for messages from others, and pushes them to the view layer when they are received.
- Parallel concurrent extension:
It seems that threads 1 and 2 run "simultaneously". This "simultaneously" can be divided into two cases: parallel and concurrent.
-
When different CPUs are specified for threads 1 and 2 on a multi-core cpu computer (SetThreadAffinityMask), the two threads are parallel;
-
When the cpu is not specified, in most cases, multiple threads of the same process seize a cpu core and execute concurrently in time slices.
What are threads in JAVA?
From the process of using QQ in scenario 1, it is not difficult to see that threads are objects that Java can "run at the same time", which are used when you want to realize several functions at the same time.
The main thread is started by default, and other threads are created by the programmer.
Multithreading shares the heap memory of the process (mainly for object instances) and only shares its own part of the stack memory (for instructions and operands).
How to implement the simplest multithreading?
Although thread is a thread class, look at the source code. Thread implements the Runnable interface. Let's start with the Runnable interface.
Runnable interface
The source code is very simple. There is only one abstract method, run
- Source code comments:
When an object * * * (not the object of Thread class) * * *, which implements the Runnable interface, creates * * * (new) * *, and starts * * * (start) a Thread, its run method will be automatically called in a separate Thread. This run method can perform any operation.
- in other words:
The object of the Thread subclass we created does not need to call the run method explicitly. After creating a new and starting the start Thread object, the run method will be called automatically
- experiment
MyThread.java
public class MyThread extends Thread{ // Because the Thread class implements the run method, the Thread subclass can be overridden @Override public void run() { System.out.println("implement run Yes"); } }
Demo1.java
public class Demo1 { public static void main(String[] args) { Thread myThread = new MyThread();// Thread object System.out.println("this is the main thread."); myThread.start();// See if run is executed automatically } }
Run result: the run of the Runnable interface is indeed executed automatically
Thread class
There are many methods and internal classes in the source code. Let's take a look at the basic.
Construction method
9 kinds!!!
There are four commonly used:
- No parameter
//Allocates a new Thread object. This constructor has the same effect as Thread (null, null, gname), where gname is a newly generated name. Automatically generated names are of the form "Thread-"+n, where n is an integer. public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } // Look at the init method called private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); } // No, init is an overloaded method, and the full version has two parameters private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals)
Since the init method called by the parameterless construction method actually has 6 parameters, it is assumed that other construction methods also change on the basis of these 6 parameters.
- With Runnable object parameters
// The Runnable object must implement the abstract run method. Passing this parameter means that the thread will automatically execute a process public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
- With name parameter
// Name refers to the thread name public Thread(String name) { init(null, null, name, 0); }
- With ThreadGroup and name parameters
public Thread(ThreadGroup group, String name) { init(group, null, name, 0);// Threads in the thread group can access the current thread group information, not those not in the current thread group. It feels a bit like a thread pool }
There are also many fields and methods in the thread group. The thread group can traverse threads to know which have been run and which are still active.
start()
Execute run()
Implement the run() method of the Runnable interface and execute it automatically after start ing
Current thread currentThread()
Thread.currentThread(); // Method to return the thread currently being executed.
Set / get thread name (set / getname)
Thread.currentThread().setName("Send a message"); Thread.currentThread().getName("Receive message"); // Method to set / get the thread name of the thread currently being executed.
Runnable is used in combination with Thread
It is not recommended to directly use Thread objects instead of Runnable objects, because:
- JAVA only has the limitation of single inheritance. If you inherit Thread, you can't inherit other classes
- Don't write subclasses of Thread
- Multiple threads (objects) can easily perform the same task
(1) A class that implements the Runnable interface
// Task class for thread execution class MyRunnable implements Runnable{ @Override public void run(){ // Task content } } // Task class object MyRunnable myRunnable = new Runnable();
(2) Object of Thread class
Thread thread = new Thread(myRunnable);//When used with Runnable, there is no need to write Thread class
Scenario 2: thread interruption
When using anti-virus software, the anti-virus is half. I want to stop anti-virus.
Why do threads interrupt?
Normally, a thread ends automatically after executing the run() method.
In actual use, users should be able to safely stop a function at any time.
What is a thread interrupt?
In the past, JAVA provided the stop() method for threads, but the external stop of a thread may generate resources that cannot be recycled (file handles, hardware resources, etc.), resulting in the continuous occupation of memory.
Therefore, using interrupt() now is equivalent to throwing an interrupt exception. The thread catches the exception and handles it accordingly.
How to implement thread interrupt safely?
Thread thread = new Thread(runnable); thread.start(); sleep(5); thread.interrupt();// Interrupt flag // Overridden run method @Override public void run(){ try{ ... }catch (InterruptException e){ ...// Release resources return;// Thread end }
Scenario 3: daemon thread
Classic daemon thread, GC garbage collector.
What is a daemon thread?
There are two types of JAVA threads: user thread and daemon thread
User thread: when a process does not contain any surviving user threads, the process ends.
Daemon thread: used to guard user threads. When the last user thread ends, all daemon threads will die automatically. For example, without the guardian (user thread), GC has no meaning.
Threads that are not specified as daemon threads are user threads.
How to implement daemon thread?
Thresd thread = new Thread(); thread.setDeamon(true);// The default is false, that is, the user thread, which must be set before start thread.start();
Thread state
Scenario 3: thread synchronization
Three windows sell tickets together. When the remaining tickets are 0, all three windows can no longer buy tickets.
Why do threads need synchronization?
Mainly thread safety issues:
The examples of queuing up for dinner, queuing into the fitting room and selling tickets at multiple windows will be chaotic if there are no restrictions, that is, different people eat the same meal, enter the same fitting room and sell the - 1st ticket.
What is thread synchronization?
In fact, it is * * * a piece of code * * *, and the thread needs * * * to queue * * * to execute this code. In this way, it can judge whether * * * needs to execute this code at the beginning of this code.
How to achieve thread synchronization?
1. Synchronized code block (object o)
//Create three threads in the main() method to buy tickets Runnable ticket = new Ticket(); //All perform the same task new Thread.start(ticket); new Thread.start(ticket); new Thread.start(ticket); //Task class class Ticket implements Runnable{ //The number of votes is shared by three threads, because it is called when it is nre Runnable private int count = 10; //The lock object is shared by three threads, because it is called when it is nre Runnable private Object o = new Object(); //Called when start() is started @Override public void run(){ //You can't write lock objects in run because each thread has its own lock. There is no need to queue up for execution. The locks of threads do not interfere with each other. You can use them if you want //Each thread cycles to buy tickets. When the number of votes is less than 0, it means that the tickets are sold out and the ticket buying should be ended while(count>0){ //Synchronization code block, which is the critical area synchronized(o){//o can be changed to this. In short, it needs to be the same object //In synchronization, it is also necessary to actually judge the number of remaining votes to reach the number of votes that do not sell - 1 if(count<0){ System.out.println("Preparing to buy tickets..."); try{ Thread.sleep(1000);//Thread sleep, in milliseconds }catch(InterruptedException e){ e.pribtStackTrace(); } count--; System.out.println("Tickets sold successfully, remaining tickets:"+count); }else{ //End the thread, return is OK break; } } } } }
2. Synchronization method synchronized
Write the contents of the synchronization code block as a method. This method is modified with synchronized, which becomes a synchronization method.
//Called when start() is started @Override public void run(){ while(true){ boolean flag = sale(); if(!flag){ break; } } } //Ticket selling process public synchronized boolean sale(){ if(count<0){ System.out.println("Preparing to buy tickets..."); try{ Thread.sleep(1000);//Unit: ms }catch(InterruptedException e){ e.pribtStackTrace(); } count--; System.out.println("Tickets sold successfully, remaining tickets:"+count); return true; } return false; }
3. Display Lock subclass ReentrantLock
//Task class class Ticket implements Runnable{ //The number of votes is shared by three threads, because it is called when it is nre Runnable private int count = 10; //Show lock objects private Lock lock = new ReentrantLock(); //Called when start() is started @Override public void run(){ //Each thread cycles to buy tickets. When the number of votes is less than 0, it means that the tickets are sold out and the ticket buying should be ended while(count>0){ //Add a lock. When other threads see a lock, they queue up lock.lock(); //Locked process, critical zone if(count<0){ System.out.println("Preparing to buy tickets..."); try{ Thread.sleep(1000);//Unit: ms }catch(InterruptedException e){ e.pribtStackTrace(); } count--; System.out.println("Tickets sold successfully, remaining tickets:"+count); }else{ //End the thread, return is OK break; } //Unlock lick.unlock() } } }
Fair lock
Who got the lock? First come, first served
//Fair lock object, pass in the true parameter private Lock lock = new ReentrantLock(fair: true);
Unfair lock
Who got the lock? Threads grab together.
Implicit locks and display locks are * * * unfair locks by default***
What is the difference between an implicit lock and a display lock?
To be added
thread deadlock
Multiple threads have entered the interdependent * * * synchronized * * method.
- resolvent
In the code that may cause deadlock, do not call other code that may cause deadlock.
Scenario 5: thread communication
Producer and consumer issues
Chef and waiter questions:
One thread wait s for another thread to notify itself.
How to realize thread communication?
In short: synchronized + wait/notify
package threadCommunication; /** * The cook cooked first and the waiter served later * The cook will cook and the waiter will serve * ...... */ public class Demo1 { public static void main(String[] args) { Food f = new Food(); Cook cook = new Cook(f); Waiter waiter = new Waiter(f); cook.start(); waiter.start(); } // Cook, cook three courses static class Cook extends Thread{ private Food f; public Cook(Food f){ this.f = f; } @Override public void run() { for (int i=0;i<3;i++){ if (i % 2 == 0) { f.setNameAndTaste("Kung Pao Chicken", "spicy "); }else { f.setNameAndTaste("Boiled Fish with Pickled Cabbage and Chili", "sour and hot"); } } } } // Waiter, serve three times static class Waiter extends Thread{ private Food f; public Waiter(Food f){ this.f = f; } @Override public void run() { for (int i=0;i<3;i++){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } f.getNameAndTaste(); } } } // food static class Food{ private String name;//Dish name private String taste;//flavor private boolean flag=true;//Sign of finished dishes // In the cooking method, it's useless to only add synchronized. This is an unfair lock. The chef may continue to seize the opportunity of execution and do not give the waiter the opportunity to serve dishes // Inconsistent with the display scene public synchronized void setNameAndTaste(String name, String taste){ if (flag){ this.name = name; // Cooking takes time try { System.out.println("In cooking, dish name:"+name+",flavor:"+taste); Thread.sleep(1000); // Thread hibernation is used here, and thread interruption may occur } catch (InterruptedException e) { e.printStackTrace(); } this.taste = taste; flag = false; this.notifyAll();// Wake up all waiting threads under this try { this.wait();//Cook waiting } catch (InterruptedException e) { e.printStackTrace(); } } } // Vegetable picking method public synchronized void getNameAndTaste() { if (!flag){ System.out.println("The name of the dish served by the waiter:"+this.name+",Taste:"+this.taste); flag = true; this.notifyAll(); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
Thread pool
Common thread steps:
Create a thread, create a task, execute a task, stop the thread (interrupt), and close the thread (the thread will be closed automatically after the run is completed)
Thread pool:
Executors.execute(new Runnable() {...// Common lambda expressions });
4 default thread pools
Cache, fixed length, single thread, fixed cycle length
1. Cache thread pool
Unlimited length
First judge whether there are idle threads in the thread pool. If not, create a new thread, which can flexibly recycle idle threads
ExecutorService service = Executors.newCachedThreadPool(nThreads: n);
2. Fixed length linear pool
Parameter specifies the thread pool length
ExecutorService service = Executors.newFixedThreadPool(nThreads: n);
3. Single thread pool
It can be executed in the specified order (FIFO, LIFO, priority)
ExecutorService service = Executors.newSingleThreadExecutor();
4. Periodic fixed length linear pool
ScheduleExecutorService service = Executors.newScheduledThreadPool(corePoolSize: n); // 2 execution methods // Execute a xx task after a certain time interval service.schedule(new Runnable() {}, 5, TimeUnit.SECONDS); // Execute xx tasks periodically and continuously service.scheduleAtFixedRate(new Runnable() {}, initDelay: n, period: m TimeUnit.SECONDS);
Interface Callable with return value
Compare the differences between Runnable interfaces:
-
The Callable interface uses generics
-
The abstract call method has a return, so you need to specify the return value type
FutureTask task object
Lambda expressions / anonymous functions
Functional programming idea
Keep only the method part, independent of the object
Make the implementation of the interface simpler. This interface can only have one method to be implemented
(parameters) -> expression // or (parameters) ->{ statements; }
- Practical examples
cachedThreadPool.execute(()->{ System.out.println("Thread Name:" + Thread.currentThread().getName()); });