线程池执行原理?
一个线程提交到线程池的处理流程如下:
- 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理新任务
- 如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,将任务放入缓冲队列
- 如果此时线程池中的数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,创建新的线程来处理新任务
- 如果线程池中的数量等于maximumPoolSize,使用 handler拒绝策略进行处理
线程池的一些参数
corePoolSize
:线程池的基本大小。线程池中会维护一个最小的线程数量corePoolSize,如果线程池中的线程数量小于corePoolSize,即使有些线程处理空闲状态,它们也不会被销毁。
poolSize
:线程池中当前线程的数量。
BlockingQueue
:存储等待运行的任务。
keepAliveTime
:空闲线程存活时间。一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁
maximumPoolSize
:线程池中允许的最大线程数。
workQueue
工作队列。新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。
handler
拒绝策略。线程池中的线程数量也达到最大值(maximumPoolSize
),这时如果有新任务提交进来,就是触发handler。
workQueue四种队列
- ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。
- LinkedBlockingQueue
基于链表的无界阻塞队列(但其实它的最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,基本不会满(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
- SynchronousQueue
因为SynchronousQueue没有容量,实际上它不是一个真正的队列。它只是维护一组线程,这些线程在等待着把元素加入或移出队列。 发送或者消费线程的行为都会被阻塞,只有当一对消费和发送线程匹配上时,才同时退出。
- PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
handler四种拒绝策略
-
AbortPolicy(默认策略)
特点:当任务无法被提交时,直接抛出RejectedExecutionException
异常。
适用场景:任务提交失败后,需要立即得知该情况并进行处理。 -
CallerRunsPolicy
特点:当任务无法被提交时,直接丢弃该任务,不抛出任何异常
适用场景:任务的重要性相对较低,或者系统资源紧张时,优先保证系统的稳定运行。 -
DiscardPolicy
特点:当任务无法被提交时,会丢弃队列中等待最久的任务,然后尝试再次提交当前任务。
适用场景:任务之间有优先级差异,或者新任务比旧任务更有价值的情况。 -
CallerRunsPolicy
特点:当任务无法被提交时,不丢弃任务,也不抛出异常,而是由调用线程(即提交任务的线程)直接执行该任务
适用场景:希望系统在高负载时能够自我调节,而不是简单地拒绝任务。
如何设置拒绝策略
在创建ThreadPoolExecutor
时,可以通过构造函数的参数来设置拒绝策略
示例:
RejectedExecutionHandler rejectedHandler = new ThreadPoolExecutor.CallerRunsPolicy();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
rejectedHandler
);
为什么要设置线程池大小
如果线程池线程数量太小,当有大量请求需要处理,系统响应比较慢,会出现任务队列大量堆积任务导致OOM。
如果线程池线程数量过大,大量线程可能会同时抢占 CPU 资源,这样会导致大量的上下文切换,从而增加线程的执行时间,影响了执行效率。
线程池大小怎么设置?
CPU密集型任务(N+1): 系统的硬盘、内存性能相对CPU要好很多时使用该设置思路。要进行大量的计算,消耗CPU资源,可以将线程数设置为N(CPU 核心数)+1
I/O密集型任务(2N): CPU性能相对硬盘、内存要好很多时使用该设置思路。CPU经常处于空闲状态,因为它在等待外部I/O操作完成。通常我们会开CPU核心数数倍的线程
线程池的类型有哪些?适用场景?
CachedThreadPool——可缓存线程池。线程的创建数量几乎没有限制
newFixedThreadPool————指定线程数量
newSingleThreadExecutor————单线程的Executor
只创建唯一的线程来执行任务。保证任务按照指定顺序(FIFO,LIFO,优先级)执行
newScheduleThreadPool——定时线程池支持定时及周期性任务执行。