线程池(Thread Pool)是一种管理多个线程的机制,它通过重用一组已创建的线程来执行多个任务,从而减少线程创建和销毁的开销,提高系统性能和资源利用率。在 Java 中,线程池由 java.util.concurrent 包中的 ThreadPoolExecutor 类提供。
线程池(Thread Pool)是一种管理多个线程的机制,它通过重用一组已创建的线程来执行多个任务,从而减少线程创建和销毁的开销,提高系统性能和资源利用率。在 Java 中,线程池由 java.util.concurrent 包中的 ThreadPoolExecutor 类提供。下面详细介绍线程池的原理、组成部分和工作机制。
1. 线程池简介
1.1 线程池核心组成部分
-
核心线程数(corePoolSize):
- 线程池中保持的核心线程数量。即使这些线程处于空闲状态,它们也不会被销毁,除非设置了
allowCoreThreadTimeOut。
- 线程池中保持的核心线程数量。即使这些线程处于空闲状态,它们也不会被销毁,除非设置了
-
最大线程数(maximumPoolSize):
- 线程池中允许的最大线程数量。当工作队列已满且请求数超过核心线程数时,线程池会创建新线程,直到总线程数达到最大线程数。
-
工作队列(workQueue):
- 用于保存等待执行的任务的队列。常用的队列类型有
LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue等。
- 用于保存等待执行的任务的队列。常用的队列类型有
-
线程工厂(ThreadFactory):
- 用于创建新线程的工厂,可以定制线程的创建过程,例如设置线程名称、优先级等。
-
拒绝策略(RejectedExecutionHandler):
- 当任务无法被线程池接受时,执行的处理策略。常见的策略有
AbortPolicy(默认)、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。
- 当任务无法被线程池接受时,执行的处理策略。常见的策略有
1.2 线程池的工作机制
-
提交任务:
- 当客户端向线程池提交任务时,线程池会根据当前的线程数和工作队列的状态决定如何处理该任务。
-
处理任务的步骤:
- 核心线程数未达到 corePoolSize:通过线程处理任务。
- 核心线程数已达到 corePoolSize:将任务加入工作队列。
- 工作队列已满:若线程数未达到 maximumPoolSize,创建新线程处理任务。
- 线程数已达到 maximumPoolSize:执行拒绝策略。
-
线程的重用:
- 当线程完成任务后,它不会被销毁,而是返回线程池成为空闲线程,等待处理新的任务。
1.3 线程池的状态转换
线程池的状态可以通过 ThreadPoolExecutor 的内部状态字段进行控制,主要包括以下几种状态:
- RUNNING:可以接收新任务并处理队列中的任务。
- SHUTDOWN:不接收新任务,但会处理队列中的任务。
- STOP:不接收新任务,也不处理队列中的任务,并中断正在执行的任务。
- TIDYING:所有任务都已终止,
workQueue为空,正在执行terminated()钩子方法。 - TERMINATED:
terminated()钩子方法执行完成。
2. 线程池的任务场景
线程池有几个关键参数:核心线程数、最大线程数、回收时间、工作队列等。
接下来将使用一个自定义线程池Demo的方式来讲解线程池的工作的流程,代码如下:
package hero.mps.basic;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPollExecutorTest {
// 核心线程池大小
private static int corePoolSize = 10;
// 最大线程池大小
private static int maxPollSize = 20;
// 线程空闲时间(秒)
private static int keepAliveTime = 5;
// 阻塞队列大小
private static int blockQueueSize = 30;
// 任务总数
private static int taskNUm = 41;
// 已执行的任务数量(使用AtomicInteger保证线程安全)
private static AtomicInteger taskNumExecuted;
/**
* 任务数≤核心线程,线程池工作的线程=任务数
* 核心线程数+队列容量<任务数≤最大线程数+队列容量,线程池工作的线程=任务数-队列容量
* @param args
*/
public static void main(String[] args) {
// 初始化已执行的任务数量为0
taskNumExecuted = new AtomicInteger(0);
// 创建一个ThreadPoolExecutor线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize,
maxPollSize,
keepAliveTime,
TimeUnit.SECONDS,
// 使用容量为blockQueueSize的ArrayBlockingQueue作为阻塞队列
new ArrayBlockingQueue<>(blockQueueSize),
// 设置线程池拒绝策略为AbortPolicy
new ThreadPoolExecutor.AbortPolicy()
);
// 打印总任务数
System.out.println("总任务数:" + taskNUm);
// 记录开始时间
long startTime = System.currentTimeMillis();
// 循环提交任务到线程池
for (int i = 0; i < taskNUm; i++) {
int finalI = i;
try {
// 提交任务到线程池执行
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
try {
// 模拟任务执行耗时
Thread.sleep(500);
} catch (InterruptedException e) {
// 如果线程被中断,则抛出运行时异常
throw new RuntimeException(e);
}
// 打印当前线程名以及已执行的任务编号
System.out.println(Thread.currentThread().getName() + "正在执行任务" + String.valueOf(taskNumExecuted.getAndAdd(1)));
}
});
} catch (Exception e) {
// 捕获异常并打印堆栈信息
e.printStackTrace();
// 更新总任务数为当前线程池活跃线程数加阻塞队列中的任务数
taskNUm = threadPoolExecutor.getActiveCount() + threadPoolExecutor.getQueue().size();
}
}
// 记录结束时间
long endTime = System.currentTimeMillis();
// 等待所有任务执行完成
while (threadPoolExecutor.getCompletedTaskCount() < taskNUm) {
endTime = System.currentTimeMillis();
}
// 打印任务执行完成数和总耗时
System.out.println("任务执行完成数:" + taskNumExecuted + "\n"+"总耗时:"+(endTime-startTime)+"ms");
// 关闭线程池
threadPoolExecutor.shutdown();
}
}
说明:我们DEMO中新建了个线程池,核心线程数10,最大线程数20,任务队列容量30。
执行每个任务场景只需修改 taskNUm值即可。
场景一:提交5个任务,执行总耗时500ms
taskNUm= 5;
执行结果:
总任务数:5
pool-1-thread-2正在执行任务0
pool-1-thread-4正在执行任务2
pool-1-thread-1正在执行任务3
pool-1-thread-5正在执行任务1
pool-1-thread-3正在执行任务4
任务执行完成数:5
总耗时:513ms
分析:核心线程数为10,也就是说有10个线程数长期处于活动状态,即来任务立马就能执行,任务数5 < 核心线程数10,所以,5个任务立马执行完成,且是多线程并行执行,所以任务执行总耗时 = 500ms
注:我们日志输出的是513ms,因为代码执行也是要花时间的。
场景二:提交10个任务,执行总耗时500ms
taskNUm= 10;
执行结果:
总任务数:10
pool-1-thread-6正在执行任务2
pool-1-thread-1正在执行任务5
pool-1-thread-4正在执行任务4
pool-1-thread-10正在执行任务3
pool-1-thread-5正在执行任务1
pool-1-thread-9正在执行任务0
pool-1-thread-2正在执行任务6
pool-1-thread-7正在执行任务7
pool-1-thread-3正在执行任务9
pool-1-thread-8正在执行任务8
任务执行完成数:10
总耗时:513ms
分析:任务数10 <= 核心线程数10,10个任务立马执行完成,所以任务执行总耗时 = 500ms。
场景三:提交11个任务,执行总耗时1000ms
taskNUm= 11;
执行结果:
总任务数:11
pool-1-thread-10正在执行任务0
pool-1-thread-1正在执行任务9
pool-1-thread-4正在执行任务4
pool-1-thread-7正在执行任务6
pool-1-thread-2正在执行任务8
pool-1-thread-3正在执行任务5
pool-1-thread-5正在执行任务7
pool-1-thread-8正在执行任务1
pool-1-thread-6正在执行任务2
pool-1-thread-9正在执行任务3
pool-1-thread-10正在执行任务10
任务执行完成数:11
总耗时:1029ms
分析:任务执行总耗时 = 1000ms,别惊讶,这里是很多人没有搞懂线程池运行机制的关键点,虽然任务只多个一个,但是地11个任务不是立马执行的,核心线程数为10,第11个任务会进入到任务队列,等核心线程有空出来后会从任务队列中取出任务再来执行,因此任务总耗时 = 1000ms。
场景四:提交20个任务,执行总耗时1000ms
taskNUm= 20;
执行结果:
总任务数:20
pool-1-thread-2正在执行任务3
pool-1-thread-1正在执行任务9
pool-1-thread-6正在执行任务8
pool-1-thread-8正在执行任务7
pool-1-thread-4正在执行任务4
pool-1-thread-5正在执行任务6
pool-1-thread-3正在执行任务5
pool-1-thread-9正在执行任务0
pool-1-thread-10正在执行任务1
pool-1-thread-7正在执行任务2
pool-1-thread-1正在执行任务14
pool-1-thread-2正在执行任务19
pool-1-thread-8正在执行任务15
pool-1-thread-3正在执行任务13
pool-1-thread-10正在执行任务10
pool-1-thread-5正在执行任务12
pool-1-thread-7正在执行任务18
pool-1-thread-4正在执行任务17
pool-1-thread-9正在执行任务11
pool-1-thread-6正在执行任务16
任务执行完成数:20
总耗时:1023ms
分析:任务执行总耗时 = 1000ms,此处与前一个场景一样,第11到第20共10个任务会进入到任务队列,等核心线程有空出来后会从任务队列中取出任务再来执行,因为有10个核心线程,前10个任务执行完成后,任务队列中的10个任务正好由空出的10个核心线程来执行,因此任务总耗时 = 1000ms。
场景五: 提交30个任务,执行总耗时1500ms
taskNUm= 30;
执行结果:
总任务数:30
pool-1-thread-9正在执行任务3
pool-1-thread-5正在执行任务9
pool-1-thread-4正在执行任务8
pool-1-thread-6正在执行任务6
pool-1-thread-10正在执行任务7
pool-1-thread-7正在执行任务4
pool-1-thread-2正在执行任务0
pool-1-thread-8正在执行任务1
pool-1-thread-3正在执行任务5
pool-1-thread-1正在执行任务2
pool-1-thread-1正在执行任务15
pool-1-thread-7正在执行任务19
pool-1-thread-10正在执行任务17
pool-1-thread-2正在执行任务18
pool-1-thread-4正在执行任务16
pool-1-thread-6正在执行任务12
pool-1-thread-8正在执行任务14
pool-1-thread-5正在执行任务11
pool-1-thread-3正在执行任务13
pool-1-thread-9正在执行任务10
pool-1-thread-10正在执行任务22
pool-1-thread-4正在执行任务24
pool-1-thread-3正在执行任务29
pool-1-thread-5正在执行任务26
pool-1-thread-6正在执行任务25
pool-1-thread-7正在执行任务23
pool-1-thread-1正在执行任务21
pool-1-thread-2正在执行任务20
pool-1-thread-8正在执行任务28
pool-1-thread-9正在执行任务27
任务执行完成数:30
总耗时:1529ms
分析:任务执行总耗时 = 1500ms,此处与前一个场景一样,第11到第30共20个任务会进入到任务队列,等核心线程有空出来后会从任务队列中取出任务再来执行,因为有10个核心线程,前10个任务执行完成后,从任务队列中取出10个任务由空出的10个核心线程来执行,执行完后在取出10个任务来执行,因此任务总耗时 = 1500ms。
场景六: 提交40个任务,执行总耗时2000ms
taskNUm= 40;
执行结果:
总任务数:40
pool-1-thread-10正在执行任务1
pool-1-thread-8正在执行任务3
pool-1-thread-4正在执行任务5
pool-1-thread-7正在执行任务8
pool-1-thread-3正在执行任务9
pool-1-thread-1正在执行任务7
pool-1-thread-2正在执行任务6
pool-1-thread-9正在执行任务2
pool-1-thread-6正在执行任务0
pool-1-thread-5正在执行任务4
pool-1-thread-2正在执行任务13
pool-1-thread-8正在执行任务19
pool-1-thread-7正在执行任务17
pool-1-thread-10正在执行任务18
pool-1-thread-4正在执行任务16
pool-1-thread-6正在执行任务15
pool-1-thread-5正在执行任务10
pool-1-thread-9正在执行任务12
pool-1-thread-3正在执行任务14
pool-1-thread-1正在执行任务11
pool-1-thread-5正在执行任务21
pool-1-thread-2正在执行任务26
pool-1-thread-10正在执行任务25
pool-1-thread-9正在执行任务20
pool-1-thread-4正在执行任务27
pool-1-thread-6正在执行任务22
pool-1-thread-3正在执行任务23
pool-1-thread-1正在执行任务24
pool-1-thread-8正在执行任务29
pool-1-thread-7正在执行任务28
pool-1-thread-10正在执行任务32
pool-1-thread-3正在执行任务36
pool-1-thread-7正在执行任务39
pool-1-thread-1正在执行任务38
pool-1-thread-8正在执行任务37
pool-1-thread-2正在执行任务35
pool-1-thread-5正在执行任务31
pool-1-thread-4正在执行任务33
pool-1-thread-6正在执行任务34
pool-1-thread-9正在执行任务30
任务执行完成数:40
总耗时:2033ms
分析:任务执行总耗时 = 2000ms,此处与前一个场景一样。
场景七:提交41个任务,执行总耗时2000ms
taskNUm= 41;
执行结果:
总任务数:41
pool-1-thread-6正在执行任务0
pool-1-thread-2正在执行任务6
pool-1-thread-11正在执行任务9
pool-1-thread-8正在执行任务2
pool-1-thread-3正在执行任务10
pool-1-thread-7正在执行任务8
pool-1-thread-4正在执行任务7
pool-1-thread-10正在执行任务5
pool-1-thread-1正在执行任务4
pool-1-thread-5正在执行任务3
pool-1-thread-9正在执行任务1
pool-1-thread-6正在执行任务17
pool-1-thread-9正在执行任务21
pool-1-thread-1正在执行任务20
pool-1-thread-5正在执行任务19
pool-1-thread-11正在执行任务15
pool-1-thread-7正在执行任务14
pool-1-thread-4正在执行任务18
pool-1-thread-3正在执行任务16
pool-1-thread-8正在执行任务11
pool-1-thread-10正在执行任务13
pool-1-thread-2正在执行任务12
pool-1-thread-10正在执行任务25
pool-1-thread-9正在执行任务31
pool-1-thread-6正在执行任务32
pool-1-thread-3正在执行任务27
pool-1-thread-1正在执行任务30
pool-1-thread-5正在执行任务29
pool-1-thread-2正在执行任务28
pool-1-thread-8正在执行任务22
pool-1-thread-11正在执行任务23
pool-1-thread-4正在执行任务26
pool-1-thread-7正在执行任务24
pool-1-thread-8正在执行任务36
pool-1-thread-9正在执行任务39
pool-1-thread-10正在执行任务40
pool-1-thread-1正在执行任务35
pool-1-thread-6正在执行任务38
pool-1-thread-3正在执行任务37
pool-1-thread-5正在执行任务34
pool-1-thread-2正在执行任务33
任务执行完成数:41
总耗时:2033ms
分析:任务执行总耗时 = 2000ms,这里重点来了,我们知道第11到第40共30个任务进入到了任务队列中(任务队列大小为30),第41个任务就创建了一个非核心线程(最大线程数20 - 核心线程数10 = 10个非核心线程)来执行,此时线程池中的活跃线程数为11,第一批任务执行完后,会从任务队列中取出11个任务来执行,那就是 11 + 11 + 11 + 8 = 500ms * 4 = 2000ms。
场景八:提交45个任务,执行总耗时1500ms
taskNUm= 45;
执行结果:
总任务数:45
pool-1-thread-14正在执行任务3
pool-1-thread-2正在执行任务10
pool-1-thread-6正在执行任务9
pool-1-thread-5正在执行任务8
pool-1-thread-4正在执行任务13
pool-1-thread-12正在执行任务6
pool-1-thread-13正在执行任务0
pool-1-thread-10正在执行任务1
pool-1-thread-11正在执行任务4
pool-1-thread-9正在执行任务5
pool-1-thread-1正在执行任务14
pool-1-thread-3正在执行任务12
pool-1-thread-7正在执行任务11
pool-1-thread-8正在执行任务2
pool-1-thread-15正在执行任务7
pool-1-thread-1正在执行任务16
pool-1-thread-2正在执行任务21
pool-1-thread-6正在执行任务23
pool-1-thread-13正在执行任务20
pool-1-thread-10正在执行任务19
pool-1-thread-9正在执行任务18
pool-1-thread-8正在执行任务15
pool-1-thread-11正在执行任务27
pool-1-thread-15正在执行任务26
pool-1-thread-4正在执行任务29
pool-1-thread-3正在执行任务25
pool-1-thread-7正在执行任务17
pool-1-thread-14正在执行任务24
pool-1-thread-5正在执行任务22
pool-1-thread-12正在执行任务28
pool-1-thread-12正在执行任务30
pool-1-thread-4正在执行任务39
pool-1-thread-8正在执行任务43
pool-1-thread-11正在执行任务44
pool-1-thread-15正在执行任务42
pool-1-thread-2正在执行任务41
pool-1-thread-1正在执行任务40
pool-1-thread-6正在执行任务38
pool-1-thread-13正在执行任务37
pool-1-thread-5正在执行任务32
pool-1-thread-3正在执行任务36
pool-1-thread-10正在执行任务35
pool-1-thread-9正在执行任务34
pool-1-thread-7正在执行任务33
pool-1-thread-14正在执行任务31
任务执行完成数:45
总耗时:1520ms
分析:任务执行总耗时 = 1500ms,此处与前一个场景一样,我们知道第11到第40共30个任务进入到了任务队列中(任务队列大小为30),第41到第45共5个任务就创建了5个非核心线程(最大线程数20 - 核心线程数10 = 10个非核心线程)来执行,此时线程池中的活跃线程数为15,第一批任务执行完后,会从任务队列中取出15个任务来执行,那就是 15 + 15 + 15 = 500ms * 3 = 1500ms。
场景九:提交50个任务,执行总耗时1500ms
taskNUm= 50;
执行结果:
总任务数:50
pool-1-thread-17正在执行任务0
pool-1-thread-9正在执行任务7
pool-1-thread-13正在执行任务6
pool-1-thread-8正在执行任务11
pool-1-thread-14正在执行任务3
pool-1-thread-18正在执行任务2
pool-1-thread-3正在执行任务19
pool-1-thread-19正在执行任务5
pool-1-thread-10正在执行任务1
pool-1-thread-4正在执行任务18
pool-1-thread-1正在执行任务17
pool-1-thread-2正在执行任务16
pool-1-thread-7正在执行任务14
pool-1-thread-5正在执行任务15
pool-1-thread-12正在执行任务12
pool-1-thread-6正在执行任务13
pool-1-thread-11正在执行任务10
pool-1-thread-16正在执行任务8
pool-1-thread-15正在执行任务9
pool-1-thread-20正在执行任务4
pool-1-thread-4正在执行任务20
pool-1-thread-8正在执行任务32
pool-1-thread-14正在执行任务39
pool-1-thread-18正在执行任务38
pool-1-thread-13正在执行任务37
pool-1-thread-9正在执行任务36
pool-1-thread-19正在执行任务35
pool-1-thread-17正在执行任务34
pool-1-thread-3正在执行任务33
pool-1-thread-20正在执行任务31
pool-1-thread-15正在执行任务30
pool-1-thread-16正在执行任务29
pool-1-thread-7正在执行任务23
pool-1-thread-12正在执行任务24
pool-1-thread-6正在执行任务28
pool-1-thread-5正在执行任务27
pool-1-thread-2正在执行任务26
pool-1-thread-11正在执行任务25
pool-1-thread-1正在执行任务22
pool-1-thread-10正在执行任务21
pool-1-thread-9正在执行任务42
pool-1-thread-8正在执行任务48
pool-1-thread-14正在执行任务49
pool-1-thread-18正在执行任务47
pool-1-thread-13正在执行任务46
pool-1-thread-3正在执行任务43
pool-1-thread-4正在执行任务45
pool-1-thread-20正在执行任务44
pool-1-thread-19正在执行任务40
pool-1-thread-17正在执行任务41
任务执行完成数:50
总耗时:1523ms
分析:任务执行总耗时 = 1500ms,此处与前一个场景一样,我们知道第11到第40共30个任务进入到了任务队列中(任务队列大小为30),第41到第50共10个任务就创建了10个非核心线程(最大线程数20 - 核心线程数10 = 10个非核心线程)来执行,此时线程池中的活跃线程数为20,第一批任务执行完后,会从任务队列中取出20个任务来执行,那就是 20 + 20 + 10 = 500ms * 3 = 1500ms。
场景十:提交51个任务,执行总耗时1500ms
taskNUm= 51;
执行结果:
总任务数:51
java.util.concurrent.RejectedExecutionException: Task hero.mps.basic.ThreadPollExecutorTest$1@5e2de80c rejected from java.util.concurrent.ThreadPoolExecutor@1d44bcfa[Running, pool size = 20, active threads = 20, queued tasks = 30, completed tasks = 0]
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 hero.mps.basic.ThreadPollExecutorTest.main(ThreadPollExecutorTest.java:56)
pool-1-thread-14正在执行任务0
pool-1-thread-5正在执行任务14
pool-1-thread-11正在执行任务11
pool-1-thread-1正在执行任务19
pool-1-thread-8正在执行任务12
pool-1-thread-2正在执行任务13
pool-1-thread-9正在执行任务10
pool-1-thread-16正在执行任务7
pool-1-thread-6正在执行任务9
pool-1-thread-15正在执行任务5
pool-1-thread-10正在执行任务6
pool-1-thread-13正在执行任务1
pool-1-thread-19正在执行任务8
pool-1-thread-20正在执行任务3
pool-1-thread-17正在执行任务2
pool-1-thread-18正在执行任务4
pool-1-thread-4正在执行任务18
pool-1-thread-3正在执行任务17
pool-1-thread-12正在执行任务16
pool-1-thread-7正在执行任务15
pool-1-thread-14正在执行任务24
pool-1-thread-20正在执行任务29
pool-1-thread-19正在执行任务39
pool-1-thread-8正在执行任务38
pool-1-thread-11正在执行任务37
pool-1-thread-16正在执行任务36
pool-1-thread-15正在执行任务35
pool-1-thread-3正在执行任务30
pool-1-thread-10正在执行任务32
pool-1-thread-13正在执行任务34
pool-1-thread-7正在执行任务33
pool-1-thread-9正在执行任务31
pool-1-thread-6正在执行任务28
pool-1-thread-12正在执行任务21
pool-1-thread-2正在执行任务27
pool-1-thread-1正在执行任务26
pool-1-thread-4正在执行任务22
pool-1-thread-17正在执行任务23
pool-1-thread-5正在执行任务25
pool-1-thread-18正在执行任务20
pool-1-thread-13正在执行任务40
pool-1-thread-14正在执行任务49
pool-1-thread-8正在执行任务45
pool-1-thread-20正在执行任务48
pool-1-thread-11正在执行任务46
pool-1-thread-16正在执行任务43
pool-1-thread-15正在执行任务44
pool-1-thread-3正在执行任务42
pool-1-thread-10正在执行任务41
pool-1-thread-19正在执行任务47
任务执行完成数:50
总耗时:1519ms
分析:任务执行总耗时 = 1500ms,此处与前一个场景一样,由于线程池同时最大能接收50个任务(最大线程数20 + 任务队列大小30 = 50),所以第51个任务被拒绝了(线程池使用默认拒绝策略AbortPolicy),抛出了异常,DEMO中使用了try-catch捕获到了。
3. 总结
-
任务数 <= 核心线程数,线程池中工作线程数 = 任务数;
-
核心线程数 < 任务数 <= (核心线程数 + 队列容量)时,线程池中工作线程数 = 核心线程数;
-
(核心线程数 + 队列容量) < 任务数 <= (最大线程数 + 队列容量)时,线程池中工作线程数 = (任务数 - 队列容量);
2893

被折叠的 条评论
为什么被折叠?



