线程池类结构
线程池分类
ThreadPoolExecutor
预定义线程池
预定义线程池是一种基于ThreadPoolExecutor的按照不同概念的构建的线程池
- FixedThreadPool
- CachedThreadPool
- SingleThreadExecutor
- ScheduledThreadPool
- SingleThreadScheduledExecutor
线程池创建方式
通过ThreadPoolExecutor构造方法创建自定义线程池
- corePoolSize→核心线程数
- maximumPoolSize→最大线程数
- keepAliveTime→空闲线程的存活时间
- unit→空闲线程的存活时间的时间单位
- workQueue→工作队列/阻塞队列
- handle→饱和策略/拒绝策略
- threadFactory→线程工场
通过Executors工具类创建预定义线程池
- newFixedThreadPool→创建FixedThreadPool线程池
- newCachedThreadPool→创建CachedThreadPool线程池
- newSingleThreadExecutor→创建newCachedThreadPool线程池
- newScheduledThreadPool→创建newScheduledThreadPool线程池
- newSingleThreadScheduledExecutor→创建ScheduledThreadPoolExecutor线程池
线程池原理
线程池状态
RUNNING
线程池创建时的默认状态,此状态下线程池可以接受新任务并处理已添加的任务
SHUTDOWN
此状态下线程池不接受新任务但处理已添加的任务,RUNNING状态下可以通过调用shutdown方法切换到此状态
STOP
此状态下线程池不接受新任务并且不处理已添加的任务,RUNNING和SHUTDOWN状态下可以通过调用shutdownNow方法切换到此状态
TIDYING
线程池所有任务完成将切换到此状态,SHUTDOWN状态下队列和任务都为空以及STOP状态下任务为空会自动切换到此状态
TERMINATED
线程池终止时将切换到此状态,TIDYING状态下执行terminated方法会切换到此状态
ThreadPoolExecutor参数解析
corePoolSize
corePoolSize 指的是核心线程数,线程池初始化时线程数默认为 0,当有新的任务提交后,会创建新线程执行任务,如果不做特殊设置,此后线程数通常不会再小于 corePoolSize ,因为它们是核心线程,即便未来可能没有可执行的任务也不会被销毁
maximumPoolSize
随着任务量的增加,在任务队列满了之后,线程池会进一步创建新线程,最多可以达到maximumPoolSize 来应对任务多的场景,如果未来线程有空闲,大于 corePoolSize 的线程会按照设置的存活时间被合理回收。所以正常情况下,线程池中的线程数量会处在 corePoolSize 与 maximumPoolSize 的闭区间内
keepAliveTime+unit
当线程池中线程数量多于核心线程数时,而此时又没有任务可做,线程池就会检测线程的 keepAliveTime,如果超过规定的时间,空闲的线程就会被销毁,以便减少内存的占用和资源消耗。如果后期任务又多了起来,线程池也会根据规则重新创建线程,所以这是一个动态的过程,也可以用 setKeepAliveTime 方法动态改变 keepAliveTime 的参数值
threadFactory
threadFactory的作用是生产线程以便执行任务。我们可以选择使用默认的线程工厂,创建的线程都会在同一个线程组,并拥有一样的优先级,且都不是守护线程,我们也可以选择自己定制线程工厂,以方便给线程自定义命名,不同的线程池内的线程通常会根据具体业务来定制不同的线程名
workQueue
阻塞队列类结构
预定义线程与队列对应关系
| 线程池 | 队列 |
|---|---|
| FixedThreadPool | LinkedBlockingQueue |
| SingleThreadExecutor | LinkedBlockingQueue |
| CachedThreadPool | SynchronousQueue |
| ScheduledThreadPool | DelayedWorkQueue |
| SingleThreadScheduledExecutor | DelayedWorkQueue |
LinkedBlockingQueue
基于链表的无界队列,适用于FixedThreadPool和SingleThreadExecutor线程池,实际最大值为Integer#MAX_VALUE,由于 FixedThreadPool 和SingleThreadExecutor线程池的线程数是固定的,所以没有办法增加特别多的线程来处理任务,这时就需要 LinkedBlockingQueue 这样一个没有容量限制的阻塞队列来存放任务
SynchronousQueue
SynchronousQueue没有容量,是无缓冲等待队列,所以没有一个地方来暂存元素,导致每次取数据都要先阻塞,直到有数据被放入;每次放数据的时候也会阻塞,直到有消费者来取
DelayedWorkQueue
DelayedWorkQueue 的特点是内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是Heap的数据结构。之所以线程池 ScheduledThreadPool 和 SingleThreadScheduledExecutor 选择 DelayedWorkQueue,是因为它们本身正是基于时间执行任务的,而延迟队列正好可以把任务按时间进行排序,方便任务的执行
ArrayBlockingQueue
基于数组的有界队列,的最大特点就是容量是有限且固定的。这样一来,使用 ArrayBlockingQueue 且设置了合理大小的最大线程数的线程池,在任务队列放满了以后,如果线程数也已经达到了最大值,那么线程池根据规则就会拒绝新提交的任务,而不会无限增加任务或者线程数导致内存不足,可以非常有效地防止资源耗尽的情况发生
PriorityBlockingQueue
基于优先级的无界阻塞队列,可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则,可以自动扩容,队列永远不会满
handle
拒绝策略原理
在线程池运行时,在某些情况下会触发拒绝策略;
- 第一种情况是当我们调用 shutdown 等方法关闭线程池后,即便此时线程池内依然有没执行完的任务正在执行,但由于线程池已经关闭,此时如果再向线程池内提交任务,就会遭到拒绝。
- 第二种情况是线程池没有能力继续处理新提交的任务,也就是工作已经非常饱和的时候;例如新建一个线程池,使用容量上限为 20的 ArrayBlockingQueue 作为任务队列,并且指定线程池的核心线程数为 5,最大线程数为 10,假设此时有 30 个耗时任务被提交,在这种情况下,线程池会首先创建核心数量的线程,也就是5个线程来执行任务,然后往队列里去放任务,队列的 10 个容量被放满了之后,会继续创建新线程,直到达到最大线程数 10。此时线程池中一共有 30 个任务,其中 10 个任务正在被 10 个线程执行,还有 20 个任务在任务队列中等待,而且由于线程池的最大线程数量就是 10,所以已经不能再增加更多的线程来帮忙处理任务了,这就意味着此时线程池工作饱和,这个时候再提交新任务时就会被拒绝触发拒绝策略
拒绝策略类结构
AbortPolicy
默认策略,此策略会直接抛出一个类型为 RejectedExecutionException 的 RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略。
CallerRunsPolicy
当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
DiscardOldestPolicy
如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险
DiscardPolicy
当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务
预定义线程池解析
FixedThreadPool
固定线程池,主要体现在核心线程数等同于最大线程数,所以不存在空闲线程因此空闲时间为0MS
| 核心线程数 | 最大线程数 | 存活时间 | 工作线程 | 线程工场 | 拒绝策略 |
|---|---|---|---|---|---|
| 自定义 | 同核心线程数 | 0MS | LinkedBlockingQueue | DefaultThreadFactory | AbortPolicy |
/**
* 创建方法1:一个线程数参数,可指定核心线程数与最大线程数
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* 创建方法2:一个线程数参数,可指定核心线程数与最大线程数;一个线程池工场参数
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
CachedThreadPool
缓存线程池,核心线程数为0,最大线程数为
Integer.MAX_VALUE,空闲线程为60秒
| 核心线程数 | 最大线程数 | 存活时间 | 工作线程 | 线程工场 | 拒绝策略 |
|---|---|---|---|---|---|
| 0 | Integer.MAX_VALUE | 60S | SynchronousQueue | DefaultThreadFactory | AbortPolicy |
/**
* 创建方法1:无参数
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* 创建方法2:一个线程工场参数
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
SingleThreadExecutor
单线程数线程池,核心线程数和最大线程数均为1,空闲线程立即销毁
| 核心线程数 | 最大线程数 | 存活时间 | 工作线程 | 线程工场 | 拒绝策略 |
|---|---|---|---|---|---|
| 1 | 1 | 0MS | LinkedBlockingQueue | DefaultThreadFactory | AbortPolicy |
/**
* 创建方法1:无参数
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
* 创建方法2:一个线程工场参数
*/
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
ScheduledThreadPool
定时执行的线程池,核心线程数自定义,最大线程数为
Integer.MAX_VALUE,空闲线程10毫秒后销毁
| 核心线程数 | 最大线程数 | 存活时间 | 工作线程 | 线程工场 | 拒绝策略 |
|---|---|---|---|---|---|
| 自定义 | Integer.MAX_VALUE | 10MS | DelayedWorkQueue | DefaultThreadFactory | AbortPolicy |
/**
* 创建方法1: 一个线程数参数,可指定核心线程数
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
/**
* 创建方法2: 一个线程数参数,可指定核心线程数;一个线程工场参数
*/
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
SingleThreadScheduledExecutor
定时执行的单线程线程池,核心线程数和最大线程数均为1,空闲线程10毫秒后销毁
| 核心线程数 | 最大线程数 | 存活时间 | 工作线程 | 线程工场 | 拒绝策略 |
|---|---|---|---|---|---|
| 1 | Integer.MAX_VALUE | 10MS | DelayedWorkQueue | DefaultThreadFactory | AbortPolicy |
/**
* 创建方法1: 一个线程数参数,可指定核心线程数
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
/**
* 创建方法2: 一个线程数参数,可指定核心线程数;一个线程工场参数
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
ThreadPoolExecutor常用配置参数
corePoolSize核心线程数
- CPU密集型:例如加密、解密、压缩、计算等一系列需要大量耗费 CPU 资源的任务。对这样的任务一般设置的线程数为 CPU 核心数的 1~2 倍
- IO密集型:例如数据库、文件的读写,网络通信等任务,这种任务的特点是并不会特别消耗 CPU 资源,但是 IO 操作很耗时,总体会占用比较多的时间。对于这种任务最大线程数一般会大于 CPU 核心数很多倍,因为 IO 读写速度相比于 CPU 的速度而言是比较慢的,如果我们设置过少的线程数,就可能导致 CPU 资源的浪费;反之如果设置更多的线程数,那么当一部分线程正在等待 IO 的时候,它们此时并不需要 CPU 来计算,那么另外的线程便可以利用 CPU 去执行其他的任务,互不影响,这样的话在任务队列中等待的任务就会减少,可以更好地利用资源
- 计算公式:线程数 = CPU 核心数 *(1+平均等待时间/平均工作时间)
handle拒绝策略
- 对于强一致性的业务来说,需要使用默认的AbortPolicy,通过异常捕获进行业务的补偿
- 对于允许任务失败的业务来说,可以使用CallerRunsPolicy直接抛弃新添加的任务
- 对于允许任务丢失的业务来说,可以使用DiscardOldestPolicy删除阻塞队列中的队首节点以腾出空间添加新任务
线程池提交任务方法
submit
- 参数:可以接收Callable类型的任务,也可以接收Runnable类型的任务
- 返回值:有Future类型的返回值,可以通过Future.get()获取结果,但是因为Runnable的返回值为void,所以结果均为null
- 异常捕获:submit内部会捕获任务执行异常,并可以通过Future.get()获取异常信息
- 适用场景:适合关心任务执行结果希望根据执行情况进一步处理的任务作业
excute
- 参数:只可以接收Runnable类型的任务
- 返回值:无返回值
- 异常捕获:如果任务发生异常,将会抛出
- 适用:适合不关系任务执行结果的任务作业