Execurtor框架最核心的类是ThreadPoolExecutor,他是线程池的实现类,主要有四个构建
- corePool:核心线程池的大小
- maximumPool:最大线程池的大小
- BlockingQueue:用来暂时保存任务的工作队列
- keepAliveTime:非核心线程闲置超时时间
FixedThreadPool详解
FixedThreadPool被称为可重用固定线程数的线程池。源代码实例如下
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool的指定参数nThread。当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。这里的keepAliveTime设置为0L,意味着多余的空闲线程会立即停止。
SingleThreadExecutor详解
SingleThreadExecutor是使用单个worker线程的Executor。源码如下
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
SingleThreadExecutor的corePoolSize和maximumPoolSize被设置为1,其他参数与FixedThreadPool相同。SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为Integer.MAX_VALUE)。SingleThreadExecutor使用无界队列作为工作队列对线程池带来的影响与FixedThreadPool相同。
CachedThreadPool详解
CachedThreadPool是一个会根据需要创建新线程的线程池。创建源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool的corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置成Integer.MAX_VALUE,即maximumPool是无界的。这里吧keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待新任务的最长时间为60s,空闲线程超过60s将会被终止。
FixedThreadPool和SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程工作队列。CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但是CachedThreadPool中maximumPool是无界的。这意味着,如果主线程提交任务的速度高一maximumPool中线程处理任务的速度时,CacheThreadPool会不断创建线程。极端情况下CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。
线程池中任务排队策略
- 直接提交(SynchronousQueue):表示线程池不对任务进行缓存。新进的任务会直接提交给线程池,当线程池中没有空闲线程时,创建一个新的线程处理此任务。这种策略需要线程池具有无限增长的可能性。
- 有界队列(如ArrayBlockingQueue):当线程池中线程达到corePoolSize是,新进任务被放在队列里面排队等待处理。有界队列有助于防止资源耗尽,但是可能较难调整和控制。
- 无界队列(LinkedBlockingQueue):将导致在所有corePoolSize线程都忙时新任务在队列中等待。这样就不会创建的线程超过corePoolSize。每个任务独立于其他任务,即任务执行互不影响是,适合于使用无界队列。
线程池的拒绝策略
- CallerRunsPolicy:线程调用运行该任务的execute本身。该策略提供简单的反馈控制机制,能够减缓新任务的提交速度。这个策略显然不想放弃执行任务,由于池中没有任何资源了,那么就直接调用该execute的线程本身来执行。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
- AbortPolicy:处理程序操刀拒绝将抛出运行时异常RejectedExecutionException。这种策略直接抛出异常,丢弃任务
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
- DiscardPolicy:不能执行的任务将被删除,这种策略与AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
- DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序,在pool没有关闭的前提下,首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
在一般的开发中,我们会自定义拒绝策略。