ThreadPoolExecutor
简介
ThreadPoolExecutor使用线程池执行提交的任务,还维护一些基本统计信息,例如已完成任务的数量。
为了适应大多数场景,该方法提供了许多可调参数和钩子方法。但是推荐使用
Executors工厂方法构造实例:Executors.newCachedThreadPool(无限制线程池,具有自动线程回收),Executors.newFixedThreadPool(固定大小的线程池)和Executors.newSingleThreadExecutor(单个后台线程)。
手动配置调优指南
核心线程和最大线程数目
ThreadPoolExecutor根据corePoolSize(请参阅getCorePoolSize)和maximumPoolSize(请参阅getMaximumPoolSize)设置的边界自动调整池大小(请参阅getPoolSize)。
- 当一个新任务在方法execute(Runnable)中提交,并且核心线程少于corePoolSize线程运行时,即使其他工作线程处于空闲状态,也会创建一个新的线程来处理该请求。
- 如果超过corePoolSize但小于maximumPoolSize线程运行,则仅当队列已满时才会创建一个新线程。
- 通过将corePoolSize和maximumPoolSize设置为相同,可以创建一个固定大小的线程池。
- 通过将maximumPoolSize设置为本质上无限制的值(例如Integer.MAX_VALUE),可以允许池适应任意数量的并发任务。
通常核心和最大池大小只能在构建时进行设置,但也可以使用setCorePoolSize和setMaximumPoolSize动态更改池大小。
按需构造
默认情况下,只有在新任务到达时,核心线程才会被创建启动,但可以通过 prestartCoreThread 或prestartAllCoreThreads方法提前启动这些线程。
创建新线程
ThreadPoolExecutor使用ThreadFactory创建线程,默认使用Executors.defaultThreadFactory(创建的线程位于同一个ThreadGroup里,并且都使用NORM_PRIORITY优先级,非守护线程)。通过提供一个ThreadFactory,可以改变线程名、线程组、优先级、守护性质等。
如果ThreadFactory创建线程时返回null,executor仍会继续,但可能不会执行任何任务。
存活时间
如果当前线程数超过核心线程数,超出的线程如果空闲时间超过keepAliveTime指定的时间,则会被终止。可以通过 setKeepAliveTime(long, TimeUnit) 方法动态的修改keepAliveTime。
设置 Long.MAX_VALUE TimeUnit.NANOSECONDS可以是超时时间生效,通过allowCoreThreadTimeOut(boolean) 方法可以设置超时是否对核心线程有效。
排队
ThreadPoolExecutor使用BlockingQueue保存任务,该队列和线程大小交互如下:
- 如果当前线程数小于核心线程数,则Executor会创建线程用于执行任务,而不会把任务交给队列
- 如果当前线程数大于核心线程数,则Executor会把任务交给队列,而不会创建线程
- 如果任务无法入队,并且当前线程数小于最大线程数,则会创建线程用于执行任务
- 如果任务无法入队,且当前线程数大于最大线程数,则该任务会被拒绝
有三种入队的策略
- 不存储任务的队列。比如SynchronousQueue,每一个put操作必须等待一个take操作,否则不能继续添加元素。如果没有一个线程可以立即去执行任务,则任务入队会失败。
- 无界队列。比如未指定长度的LinkedBlockingQueue(默认长度为Integer.MAX_VALUE),当核心线程都在运行时,新加入的任务都会入队。
- 有界队列。比如ArrayBlockingQueue,可以避免资源耗尽,但是很难调优控制。使用大队列和小线程池可以最大限度地减少CPU使用率,操作系统资源和上下文切换开销,但可能导致人为的低吞吐量(比如很多线程都被IO阻塞)。使用小队列通常需要较大的池,这样可以提高CPU利用率,但可能会遇到不可接受的调度开销,这也降低了吞吐量。
拒绝任务
当Executor被关闭,或者线程池和队列都达到饱和时,提交的任务会被拒绝。在这两种情况下,execute方法会调用RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor) ,有四种预定义的处理策略:
1. ThreadPoolExecutor.AbortPolicy:默认策略,抛出RejectedExecutionException异常
2. ThreadPoolExecutor.CallerRunsPolicy:调用execute()方法的线程自己执行提交的任务。这提供了一个简单的反馈控制机制,将降低新任务提交的速度。
3. ThreadPoolExecutor.DiscardPolicy:任务被简单丢弃
4. ThreadPoolExecutor.DiscardOldestPolicy:如果Executor没有关闭,则队列头部的任务被删除,然后重新执行(可能会再次失败,这会导致重复执行这个逻辑)。
钩子方法
beforeExecute(Thread, Runnable) 和 afterExecute(Runnable, Throwable)会在每个任务被执行前和执行后被调用, terminated()在Executor终止的时候(无任务并且已关闭)时被调用。
如果钩子方法抛出异常,内部的工作线程可能会失败并突然终止。
队列维护
方法getQueue()允许访问工作队列以进行监视和调试。 不鼓励将此方法用于任何其他目的。 当大量排队任务被取消时,两种提供的方法,remove(Runnable)和purge可用于协助进行存储回收。
public class ThreadPoolExecutor extends AbstractExecutorService {
}