Preface
statement: All tests in this article are in the JDK1.8 environment.
This article is a summary and record I made when I was learning about multithreading in Java.
If there is something wrong, please forgive me and give pointers, thank you!
Some content of this article refers to and adopts the following content:
https://www.cnblogs.com/vipstone/p/14149065.html
https://www.bilibili.com/video/BV1dt4y1i7Gt?t=1056
1. Basic concepts
We know that the CPU execution code is executed one by one in order, so in essence, a computer with a single-core CPU will not perform multiple tasks at the same point in time. But in reality, when we are using computers, we can chat on WeChat while listening to songs. How is this done? In fact, the operating system multitasking is to allow the CPU to alternately execute multiple tasks.
For example, in a classroom, there are three groups of students in first grade, second grade, and third grade at the same time. The teacher spends one minute teaching first grade, one minute teaching second grade, and one minute teaching third grade. Take turns in this way, see Going up is like teaching three grades at the same time.
Similarly, when we use a computer, we chat on WeChat while listening to music. The CPU lets WeChat execute for 0.001 seconds and the music player executes for 0.001 seconds. In our opinion, the CPU is performing multiple tasks at the same time.
1.1 The concept of programs, processes, and threads
program : an executable file stored on a disk or other data storage device, that is, a bunch of static codes.
process : an example of an executable program running in memory
Thread : A thread is an entity of a process and is the basic unit of CPU scheduling and dispatch.
Seeing whether these concepts are very abstract and uncomfortable, let me explain the above concepts with examples.
1.2 The running example of the program
As mentioned above, when we use computers, we can chat on WeChat while listening to songs. So what is the whole process of running these software?
- If we want to use WeChat to chat, most people double-click the "WeChat" shortcut on the desktop, and double-click this shortcut to open an .exe file, this .exe file is a program, please see Picture:
- After double-clicking the .exe file, load the executable program into the memory for easy reading by the CPU, then this executable file program instance is a process. Please see the picture below:
- When we use WeChat, WeChat will do many things, such as loading the WeChat UI interface, displaying the list of WeChat friends, and chatting with friends. These can be seen as separate threads in the WeChat process.
I use a picture to summarize the whole process:
Based on the above understanding of the thread concept, is there a question, how is the thread created? With this question, let's learn how to create threads in java.
Second, the creation of threads
2.1 The concept of the Thread class
The java.lang.Thread class represents threads, and any thread is an instance of the Thread class (subclass).
2.2 Commonly used methods
Construction method | Features |
---|---|
Thread() | Use no parameters to construct objects |
Thread(String name) | Construct an object according to the name specified by the parameter |
Thread(Runnable target) | Construct an object according to the reference specified by the parameter, where Runnable is an interface type |
Thread(Runnable target, String name) | According to the parameters to specify the reference and name to construct the object |
Member method | Features |
---|---|
run() | 1. Use the Runnable reference to construct the thread object, and finally call the version in the interface when the method is called. 2. If the Runnable reference is not used to construct the thread object, nothing is done when the method is called |
start() | Used to start a thread, the Java virtual machine automatically calls the run method of the thread |
2.3 How to create
2.3.1 Custom Thread class creation
The custom class inherits the Thread class and rewrites the run method according to its own needs, and then creates an object of this class in the main class and calls the start method, thus starting a thread.
The sample code is as follows:
//创建一个自定义类SubThreadRun继承Thread类,作为一个可以备用的线程类
public class SubThreadRun extends Thread {
@Override
public void run() {
//打印1~20的整数值
for (int i = 0; i < 20 ; i ++) {
System.out.println("SubThreadRun线程中:" + i);
}
}
}
//在主方法中创建该线程并启动
public class SubThreadRunTest {
public static void main(String[] args) {
//1.申明Thread类型的引用指向子类类型的对象
Thread t1 = new SubThreadRun();
//用于启动线程,java虚拟机会自动调用该线程中的run方法
t1.start();
}
}
输出结果:
SubThreadRun线程中:0
SubThreadRun线程中:2
。。。。。。
SubThreadRun线程中:19
At this point, will everyone have the following question, look at the sample code:
public class SubThreadRunTest {
public static void main(String[] args) {
//1.申明Thread类型的引用指向子类类型的对象
Thread t1 = new SubThreadRun();
//调用run方法测试
t1.run();
}
}
输出结果:
SubThreadRun线程中:0
SubThreadRun线程中:1
。。。。。。
SubThreadRun线程中:19
We do not call the start method, but directly call the run method. We find that the result is the same as calling the start method. What is the difference between the two methods?
We also add a number that prints 1-20 to the main method, and then use the run method and the start method to test respectively. The example code is as follows:
//使用run方法进行测试
public class SubThreadRunTest {
public static void main(String[] args) {
//1.申明Thread类型的引用指向子类类型的对象
Thread t1 = new SubThreadRun();
//2.调用run方法测试
t1.run();
//打印1~20的整数值
for (int i = 0; i < 20 ; i ++) {
System.out.println("-----mian-----方法中:" + i);
}
}
}
输出结果:
SubThreadRun线程中:0
SubThreadRun线程中:1
。。。。。。//都是SubThreadRun线程中
SubThreadRun线程中:19
-----mian-----方法中:0
-----mian-----方法中:1
。。。。。。//都是-----mian-----方法中
-----mian-----方法中:19
//使用start方法进行测试
public class SubThreadRunTest {
public static void main(String[] args) {
//1.申明Thread类型的引用指向子类类型的对象
Thread t1 = new SubThreadRun();
//用于启动线程,java虚拟机会自动调用该线程中的run方法
t1.start();
//打印1~20的整数值
for (int i = 0; i < 20 ; i ++) {
System.out.println("-----mian-----方法中:" + i);
}
}
}
输出结果:
SubThreadRun线程中:0
-----mian-----方法中:0
SubThreadRun线程中:1
SubThreadRun线程中:2
-----mian-----方法中:1
。。。。。。//SubThreadRun线程和mian方法相互穿插
SubThreadRun线程中:19
-----mian-----方法中:19
As can be seen from the above example:
- When the run method is called to test, it is essentially equivalent to a call to a common member method, so the execution flow is that the code of the run method can only continue to be executed after the code of the run method is executed.
- When calling the start method to test, it is equivalent to starting another thread, plus the thread that executes the main method, there are two threads in total, these two threads are running at the same time, so the output results are interleaved. (Looking back and think about it now, do you understand that I can use qq music to listen to songs while typing comments.)
We have learned the first way of creating threads. Are there any flaws in this way of creating threads? If the SubThreadRun class has inherited a parent class, at this time we have to use this class as a custom thread class. If we still use the method of inheriting the Thread class to implement it, it will violate the concept of non-inheritance in Java. So the second creation method can avoid this problem.
2.3.2 Create by implementing Runnable interface
The custom class implements the Runnable interface and rewrites the run method, creates an object of this class as an actual parameter to construct an object of type Thread, and then uses an object of type Thread to call the start method.
The sample code is as follows:
//创建一个自定义类SubRunnableRun实现Runnable接口
public class SubRunnableRun implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20 ; i ++) {
System.out.println("SubRunnableRun线程中:" + i);
}
}
}
//在主方法中创建该线程并启动
public class SunRunnableRunTest {
public static void main(String[] args) {
//1.创建自定义类型的对象
SubRunnableRun srr = new SubRunnableRun();
//2.使用该对象作为实参构造Thread类型的对象
Thread t1 = new Thread(srr);
//3.使用Thread类型的对象调用start方法
t1.start();
//打印1~20之间的所有整数
for (int i = 0; i < 20 ; i ++) {
System.out.println("-----mian-----方法中:" + i);
}
}
}
输出结果:
SubRunnableRun线程中:0
-----mian-----方法中:0
SubRunnableRun线程中:1
SubRunnableRun线程中:2
-----mian-----方法中:1
。。。。。。//SubRunnableRun线程和mian方法相互穿插
SubRunnableRun线程中:19
-----mian-----方法中:19
Will everyone have a question here?
I also instantiated the Thread class in the main method of the SunRunnableRunTest class. Why does the thread call the run method in the SubRunnableRun class that implements the Runnable interface instead of the run method in the Thread class.
In order to solve this question, we enter the Thread class to look at the source code. The source code calling process is as follows:
- From the code in the SunRunnableRunTest class above, we can see that we create a thread to call the parameterized construction method of Thread, and the parameter is of type Runnable.
- Enter the Thread class to find the parameterized construction method, see that the construction method calls the init method, and continue to pass the target parameter as a parameter.
- After turning to the corresponding init method, it is found that the init method continues to call another overloaded init method, and continues to pass the target parameter as a parameter.
- Continuing to enter the overloaded init method, we find that the target in the parameter is assigned to the member variable target in this method.
- Then find the run method in the Thread class, and find that as long as the Thread member variable target exists, the run method in the target is called.
By viewing the source code, we can know why the Thread class we created calls the run method in the Runnable class.
2.3.3 The creation of anonymous inner classes
Both of the above two ways of creating threads need to create a separate class to inherit the Thread class or implement the Runnable interface, and override the run method. An anonymous inner class can create a custom thread without creating a separate class.
The sample code is as follows:
public class ThreadNoNameTest {
public static void main(String[] args) {
//匿名内部类的语法格式:父类/接口类型 引用变量 = new 父类/接口类型 {方法的重写};
//1.使用继承加匿名内部类的方式创建并启动线程
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("继承Thread类方式创建线程...");
}
};
t1.start();
//2.使用接口加匿名内部类的方式创建并启动线程
Runnable ra = new Runnable() {
@Override
public void run() {
System.out.println("实现Runnable接口方式实现线程...");
}
};
Thread t2 = new Thread(ra);
t2.start();
}
}
These two methods of using anonymous internal classes to create threads can continue to simplify the code, especially the method of creating threads using the Runnable interface, which can be simplified using Lambda expressions.
The sample code is as follows:
public class ThreadNoNameTest {
public static void main(String[] args) {
//1.使用继承加匿名内部类简化后的方式创建并启动线程
new Thread() {
@Override
public void run() {
System.out.println("简化后继承Thread类方式创建线程...");
}
}.start();
//2.使用接口加匿名内部类简化后的方式创建并启动线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("简化后实现Runnable接口方式实现线程...");
}
}).start();
//2-1.对于接口加匿名内部创建线程,可以继续使用lambda表达式进行简化。
//Java8开始支持lambda表达式:(形参列表) -> {方法体;}
Runnable ra = () -> System.out.println("lambda表达式简化实现Runnable接口方式实现线程...");
new Thread(ra).start();
//继续简化
new Thread(() -> System.out.println("lambda表达式简化实现Runnable接口方式实现线程...")).start();
}
}
2.3.4 Created by implementing the Callable interface
Through the above examples, we have learned two ways to create threads, but there is a problem with these two ways of creating threads, that is, the run method has no return value, so if we want to give a result after the thread ends, then You need to implement the Callable interface to create threads.
(1) Callable interface
Starting from Java5, the third way to create threads is to implement the java.util.concurrent.Callable interface.
Common methods are as follows:
Member method | Features |
---|---|
V call() | The result of the calculation, if the result cannot be calculated, an exception is thrown |
We know that to start a thread, only create a Thread class and call the start method. If you want to call the call method in the Callable interface when the thread starts, you have to use the FutureTask class.
(2) FutureTask class
The java.util.concurrent.FutureTask class implements the RunnableFuture interface. The RunnableFuture interface is a combination of Runnable and Future. As a Future, FutureTask can perform asynchronous calculations, check whether the asynchronous program has been executed, and start and cancel the program, and obtain The final execution result of the program can also be used to obtain the return result after calling the method.
Common methods are as follows:
Construction method | Features |
---|---|
FutureTask(Callable<v> callable) | Create a FutureTask that will execute the given Callable at runtime |
Member method | Features |
---|---|
V get() | Get the result calculated by the call method |
It can be understood from the above concept that a construction method of the FutureTask class takes Callable as a parameter, and then the FutureTask class is a subclass of Runnable, so the FutureTask class can be used as a parameter of the Thread class. In this way, we can create a thread and call the call method in the Callable interface.
The example code is as follows:
public class ThreadCallableTest implements Callable {
@Override
public Object call() throws Exception {
//计算1 ~ 10000之间的累加和并打印返回
int sum = 0;
for (int i = 0; i <= 10000; i ++) {
sum += i;
}
System.out.println("call方法中的结果:" + sum);
return sum;
}
public static void main(String[] args) {
ThreadCallableTest tct = new ThreadCallableTest();
FutureTask ft = new FutureTask(tct);
Thread t1 = new Thread(ft);
t1.start();
Object ob = null;
try {
ob = ft.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("main方法中的结果:" + ob);
}
}
输出结果:
call方法中的结果:50005000
main方法中的结果:50005000
2.3.5 Creation of Thread Pool
thread pool origin :
Before talking about the thread pool, let’s first tell a story. A boss opens a restaurant, but this restaurant is very strange. Every time a customer comes, the boss hires a new chef to cook. When the customer leaves, the boss directly Quit the chef. If it is in this way of operation, the boss is busy recruiting chefs every day and can't do anything.
Regarding the story mentioned above, our real-life restaurant owners are not so stupid. They hire several chefs to wait in the kitchen before opening the store, and when customers come, they will cook and serve them directly. After leaving, the chef stayed in the back kitchen, thus liberating the boss.
Now let’s talk about the origin of the thread pool: For example, in server programming, if a new worker thread is assigned to each client, and when the communication between the worker thread and the client ends, the thread is destroyed, which requires frequent creation And destroy the worker thread. If there are too many clients accessing the server, it will seriously affect the performance of the server.
So how do we liberate the server? By the way, just like the restaurant owner mentioned above, create a back kitchen and let the chef wait. Relative to the server, a thread pool is created, the thread is waiting, waiting for the client to connect, and after the client ends the communication, the server does not close the thread, but returns to the thread to stand by. This frees up the server.
concept thread pool :
First, create some threads. Their collection is called the thread pool. When the server receives a client request, it takes a spare thread from the thread pool to serve it. After the service is completed, the thread is not closed, but the thread is returned In the thread pool.
related classes and methods :
- There are a total of 7 methods for creating thread pools, but in general they can be divided into 2 categories:
Executors is a tool class and thread pool factory class, used to create and return different types of thread pools, the commonly used methods are as follows:
return value method Features static ExecutorService newFixedThreadPool(int nThreads) Create a fixed-size thread pool, if the number of tasks exceeds the number of threads, the excess part will wait in the queue static ExecutorService newCachedThreadPool() Create a thread pool where new threads can be created as needed. If the number of tasks at the same time is greater than the number of threads, new threads can be created according to the number of tasks. If the task execution is completed, the threads will be recycled after a period of caching. static ExecutorService newSingleThreadExecutor() Create a thread pool with a single number of threads to ensure a first-in, first-out execution order static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) Create a thread pool that can perform delayed tasks static ScheduledExecutorService newSingleThreadScheduledExecutor() Create a single-threaded thread pool that can perform delayed tasks static ExecutorService newWorkStealingPool() Create a thread pool for preemptive execution (the order of task execution is uncertain) [Added in JDK 1.8] ThreadPoolExecutor creates a thread pool through the construction method. Up to 7 parameters can be set. The construction method to create a thread pool is as follows:
Construction method Features ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) Create a thread pool through the most primitive method After the thread pool is created by the above two methods, the ExecutorService interface can be used to receive it. It is the real thread pool interface. The main implementation class is ThreadPoolExecutor. The common methods are as follows:
Method declaration Features void execute(Runnable command) Execute tasks and commands, usually used to execute Runnable <T> Future<T> submit(Callable<T> task) Execute tasks and commands, usually used to execute Callable void shutdown() Start orderly shutdown
code example :
Use the newFixedThreadPool method to create a thread pool
public class FixedThreadPool { public static void main(String[] args) { // 创建含有两个线程的线程池 ExecutorService threadPool = Executors.newFixedThreadPool(2); // 创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("线程:" + Thread.currentThread().getName() + "执行了任务!"); } }; // 线程池执行任务 threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); } } 输出结果: 线程:pool-1-thread-2执行了任务! 线程:pool-1-thread-1执行了任务! 线程:pool-1-thread-2执行了任务! 线程:pool-1-thread-1执行了任务!
It can be seen from the results that these four tasks are executed by two fixed threads in the thread pool, and the thread pool will not create new threads to perform tasks.
Use the newCachedThreadPool method to create a thread pool
public class cachedThreadPool { public static void main(String[] args) { //1.创建线程池 ExecutorService executorService = Executors.newCachedThreadPool(); //2.设置任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("线程:" + Thread.currentThread().getName() + "执行了任务!"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { } } }; //3.执行任务 for (int i = 0; i < 100; i ++) { executorService.execute(runnable); } } } 输出结果: 线程:pool-1-thread-1执行了任务! 线程:pool-1-thread-4执行了任务! 线程:pool-1-thread-3执行了任务! 线程:pool-1-thread-2执行了任务! 线程:pool-1-thread-5执行了任务! 线程:pool-1-thread-7执行了任务! 线程:pool-1-thread-6执行了任务! 线程:pool-1-thread-8执行了任务! 线程:pool-1-thread-9执行了任务! 线程:pool-1-thread-10执行了任务!
It can be seen from the results that the thread pool creates the corresponding number of threads according to the number of tasks.
Use the newSingleThreadExecutor method to create a thread pool
public class singleThreadExecutor { public static void main(String[] args) { //创建线程池 ExecutorService executorService = Executors.newSingleThreadExecutor(); //执行任务 for (int i = 0; i < 10; i ++) { final int task = i + 1; executorService.execute(()->{ System.out.println("线程:" + Thread.currentThread().getName() + "执行了第" + task +"任务!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } } } 输出结果: 线程:pool-1-thread-1执行了第1任务! 线程:pool-1-thread-1执行了第2任务! 线程:pool-1-thread-1执行了第3任务! 线程:pool-1-thread-1执行了第4任务! 线程:pool-1-thread-1执行了第5任务! 线程:pool-1-thread-1执行了第6任务! 线程:pool-1-thread-1执行了第7任务! 线程:pool-1-thread-1执行了第8任务! 线程:pool-1-thread-1执行了第9任务! 线程:pool-1-thread-1执行了第10任务!
It can be seen from the results that the threads created by this method can guarantee the order of task execution.
Use the newScheduledThreadPool method to create a thread pool
public class ScheduledThreadPool { public static void main(String[] args) { //创建包含2个线程的线程池 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2); //记录创建任务时的当前时间 SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date startTime = new Date(); String start = formatter.format(startTime); System.out.println("创建任务时的时间:" + start); //创建任务 Runnable runnable = new Runnable() { @Override public void run() { Date endTime = new Date(); String end = formatter.format(endTime); System.out.println("线程:" + Thread.currentThread().getName() + "任务执行的时间为:" + end); } }; //执行任务(参数:runnable-要执行的任务,2-从现在开始延迟执行的时间,TimeUnit.SECONDS-延迟参数的时间单位) for(int i = 0; i < 2; i ++) { scheduledExecutorService.schedule(runnable,2, TimeUnit.SECONDS); } } } 输出结果: 创建任务的时间:2021-04-19 19:26:18 线程:pool-1-thread-1任务执行的时间为:2021-04-19 19:26:20 线程:pool-1-thread-2任务执行的时间为:2021-04-19 19:26:20
It can be seen from the results that the thread pool created by this method can allocate existing threads to perform some tasks that need to be delayed.
Use the newSingleThreadScheduledExecutor method to create a thread pool
public class SingleThreadScheduledExecutor { public static void main(String[] args) { //创建线程池 ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); //创建任务 Date startTime = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String start = formatter.format(startTime); System.out.println("创建任务的时间:" + start); Runnable runnable = new Runnable() { @Override public void run() { Date endTime = new Date(); String end = formatter.format(endTime); System.out.println("线程:" + Thread.currentThread().getName() + "任务执行的时间为:" + end); } }; //执行任务 for(int i = 0; i < 2; i ++) { scheduledExecutorService.schedule(runnable,2, TimeUnit.SECONDS); } } } 输出结果: 创建任务的时间:2021-04-19 19:51:58 线程:pool-1-thread-1任务执行的时间为:2021-04-19 19:52:00 线程:pool-1-thread-1任务执行的时间为:2021-04-19 19:52:00
It can be seen from the results that the thread pool created by this method has only one thread, and this thread executes some tasks that need to be delayed.
Use the newWorkStealingPool method to create a thread pool
public class newWorkStealingPool { public static void main(String[] args) { //创建线程池 ExecutorService executorService = Executors.newWorkStealingPool(); //执行任务 for (int i = 0; i < 4; i ++) { final int task = i + 1; executorService.execute(()->{ System.out.println("线程:" + Thread.currentThread().getName() + "执行了第" + task +"任务!"); }); } //确保任务被执行 while (!executorService.isTerminated()) { } } } 输出结果: 线程:ForkJoinPool-1-worker-9执行了第1任务! 线程:ForkJoinPool-1-worker-4执行了第4任务! 线程:ForkJoinPool-1-worker-11执行了第3任务! 线程:ForkJoinPool-1-worker-2执行了第2任务!
It can be seen from the results that this method will create a thread pool with enough threads to maintain the corresponding level of parallelism, and tasks will be executed preemptively. (The order of task execution is uncertain)
Use ThreadPoolExecutor to create a thread pool
Before writing the sample code, let me talk about a life example (go to the bank for business):
Describe the business scenario: the bank has a total of 4 windows, only two are open today, and then there are a total of 3 positions in the waiting area. As shown below:
- If the bank handles the business at the same time there are less than or equal to 5 people, then it happens that 2 people do it first, and the others wait in the waiting area. As shown below:
- If the number of people handling the business at the bank is equal to 6 people, the bank will open the third window to handle the business. As shown below:
- If the number of people handling the business at the bank is equal to 7 people, the bank will open the No. 4 window to handle the business. As shown below:
- If the bank handles more than 7 people at the same time, the lobby manager of the bank will tell the people behind that the branch is full and please go to another branch to handle it.
Now let's take a look at our ThreadPoolExecutor construction method, which can set up to 7 parameters:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
Parameter introduction:
- corePoolSize : the number of core threads, the threads that have always existed in the thread pool (corresponding to the bank's business model: the window opened at the beginning)
- maximumPoolSize : the maximum number of threads, the maximum number of threads that can be created in the thread pool, several threads other than the number of core threads will be created after the task queue of the thread pool is full (corresponding to the bank handling business model: all windows)
- keepAliveTime : the survival time of the maximum number of threads. When there is no task for a long time, the thread pool will destroy some threads and keep the core threads
: The unit of time is the unit of the third parameter. These two parameters are combined into the survival time of the maximum number of threads
- TimeUnit.DAYS: days
- TimeUnit.HOURS: hours
- TimeUnit.MINUTES: minutes
- TimeUnit.SECONDS: seconds
- TimeUnit.MILLISECONDS: milliseconds
- TimeUnit.MICROSECONDS: microseconds
- TimeUnit.NANOSECONDS: Nanoseconds
workQueue : waiting queue, used to save tasks waiting in the thread pool (corresponding to the bank's business model: waiting area)
- ArrayBlockingQueue: A bounded blocking queue backed by an array.
- LinkedBlockingQueue: A bounded blocking queue composed of linked lists.
- SynchronousQueue: The blocking queue does not store tasks, but directly submits them to the thread, so that the submitted task will be formed. If there is an idle thread, the idle thread will be used for processing, otherwise a new thread will be created to process the task.
- PriorityBlockingQueue: an unbounded blocking queue with priority, each time it leaves the queue, the element with the highest or lowest priority is returned
- DelayQueue: An unbounded blocking queue that uses priority queues to support delayed acquisition of elements. Elements can only be extracted from the delay when the delay expires. The actual use: Taobao order business: If there is no payment within 30 minutes after placing an order Automatically cancel the order.
- LinkedTransferQueue: An unbounded blocking queue composed of a linked list structure.
- LinkedBlockingDeque: A two-way blocking queue composed of a linked list structure.
- threadFactory : thread factory, used to create threads.
handler : Rejection strategy. When the task exceeds the acceptable range of the thread pool, the strategy for processing the task is refused.
- ThreadPoolExecutor.AbortPolicy: When a task is added to the thread pool and is rejected, it will throw a RejectedExecutionException ( uses the policy default)
- ThreadPoolExecutor.CallerRunsPolicy: When a task is added to the thread pool and is rejected, the thread of the current thread pool will be called to execute the rejected task
- ThreadPoolExecutor.DiscardOldestPolicy: When a task is added to the thread pool and is rejected, the oldest task in the task queue will be discarded, which is the first to be added to the queue, and then the new task will be added.
- ThreadPoolExecutor.DiscardPolicy: If the task is rejected, this directly ignores or discards the task
When the number of tasks is less than or equal to the sum of the number of core threads + the number of waiting queues :
public class ThreadPoolExecutorTest { public static void main(String[] args) { // 创建线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); //创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "==>执行任务"); } }; // 执行任务 for (int i = 0; i < 5; i++) { threadPool.execute(runnable); } //关闭线程池 threadPool.shutdown(); } } 输出结果: pool-1-thread-2==>执行任务 pool-1-thread-1==>执行任务 pool-1-thread-2==>执行任务 pool-1-thread-1==>执行任务 pool-1-thread-2==>执行任务
It can be seen from the results that only two core threads are performing tasks.
When the number of tasks is greater than the sum of the number of core threads + the number of waiting queues, but less than or equal to the maximum number of threads :
public class ThreadPoolExecutorTest { public static void main(String[] args) { // 创建线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); //创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "==>执行任务"); } }; // 执行任务 for (int i = 0; i < 7; i++) { threadPool.execute(runnable); } //关闭线程池 threadPool.shutdown(); } } 输出结果: pool-1-thread-1==>执行任务 pool-1-thread-4==>执行任务 pool-1-thread-2==>执行任务 pool-1-thread-2==>执行任务 pool-1-thread-3==>执行任务 pool-1-thread-4==>执行任务 pool-1-thread-1==>执行任务
It can be seen from the results that the largest thread is started to execute the task.
When the number of tasks is greater than the maximum number of threads :
public class ThreadPoolExecutorTest { public static void main(String[] args) { // 创建线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); //创建任务 Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "==>执行任务"); } }; // 执行任务 for (int i = 0; i < 8; i++) { threadPool.execute(runnable); } //关闭线程池 threadPool.shutdown(); } } 输出结果: Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.zck.task18.ThreadPool.ThreadPoolExecutorTest$1@7f31245a rejected from java.util.concurrent.ThreadPoolExecutor@6d6f6e28[Running, pool size = 4, active threads = 0, queued tasks = 0, completed tasks = 7] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) at com.zck.task18.ThreadPool.ThreadPoolExecutorTest.main(ThreadPoolExecutorTest.java:25) pool-1-thread-1==>执行任务 pool-1-thread-4==>执行任务 pool-1-thread-4==>执行任务 pool-1-thread-4==>执行任务 pool-1-thread-2==>执行任务 pool-1-thread-3==>执行任务 pool-1-thread-1==>执行任务
It can be seen from the results that the task is greater than the maximum number of threads, and the rejection strategy is used to directly throw an exception.
- If the bank handles the business at the same time there are less than or equal to 5 people, then it happens that 2 people do it first, and the others wait in the waiting area. As shown below:
Three, summary
This article introduces three ways to create threads:
- The custom class inherits the Thread class and rewrites the run method to create
- The custom class implements the Runnable interface and rewrites the run method to create
- Implement Callable interface creation
Introduced seven ways to create thread pools:
- Use the newFixedThreadPool method to create a thread pool
- Use the newCachedThreadPool method to create a thread pool
- Use the newSingleThreadExecutor method to create a thread pool
- Use the newScheduledThreadPool method to create a thread pool
- Use the newSingleThreadScheduledExecutor method to create a thread pool
- Use the newWorkStealingPool method to create a thread pool
- Use ThreadPoolExecutor to create a thread pool
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。