线程池解决了两个不同的问题:
- 提升性能:它们通常在执行大量异步任务时,由于减少了每个任务的调用开销,并且它们提供了一种限制和管理资源(包括线程)的方法,使得性能提升明显;
- 统计信息:每个ThreadPoolExecutor保持一些基本的统计信息,例如完成的任务数量。
JDK中的实现
Java库类中提供了newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor、newScheduledThreadPool等多种线程池。
逻辑关系

- Executor:执行器接口
- ExecutorService:在Executor的基础上提供了执行器的生命周期管理,任务异步执行等功能
- ScheduledExecutorService:在Excutor的基础上提供了任务的延迟执行/周期执行的功能
- Excutors:生产具体的执行器静态工厂
- ThreadFactory:线程工程,用于创建单个线程
- AbstractExcutorService:ExecutorService的抽象实现,为各类执行器的实现提供基础
- ThreadPoolExcutor:最常用的Excutor,以线程池的方式管理线程
通过Executors以静态方法的方式直接调用,实质上是它们最终调用的是ThreadPoolExecutor的构造方法。
ThreadPoolExecutor解析
下面来看看ThreadPoolExecutor的构造方法的代码
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}- corePoolSize:线程池维护线程的最小数量。
- maximumPoolSize:线程池维护线程的最大数量。
- keepAliveTime:空闲线程的存活时间。
- TimeUnit unit:时间单位,现有纳秒,微秒,毫秒,秒枚举值。
- BlockingQueue<Runnable> workQueue:持有等待执行的任务队列,它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种。
- handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务;
- threadFactory:线程工厂,用于创建线程。
线程池对任务的处理流程
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时:
- 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
- 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
workQueue任务队列
Direct handoffs 直接握手队列
Direct handoffs 的一个很好的默认选择是 SynchronousQueue,它将任务交给线程而不需要保留。这里,如果没有线程立即可用来运行它,那么排队任务的尝试将失败,因此将构建新的线程。
此策略在处理可能具有内部依赖关系的请求集时避免锁定。Direct handoffs 通常需要无限制的maximumPoolSizes来避免拒绝新提交的任务。 但得注意,当任务持续以平均提交速度大余平均处理速度时,会导致线程数量会无限增长问题。- Unbounded queues 无界队列
当所有corePoolSize线程繁忙时,使用无界队列(如LinkedBlockingQueue)将导致新任务在队列中等待,从而导致maximumPoolSize的值没有任何作用。当每个任务互不影响,完全独立于其他任务时,这可能是合适的; 例如,在网页服务器中, 这种队列方式可以用于平滑瞬时大量请求。但得注意,当任务持续以平均提交速度大余平均处理速度时,会导致队列无限增长问题。 - Bounded queues 有界队列
一个有界的队列(例如,一个ArrayBlockingQueue)和有限的maximumPoolSizes配置有助于防止资源耗尽,但是难以控制。队列大小和maximumPoolSizes需要相互权衡。
Rejected tasks 拒绝任务
拒绝任务有两种情况:1. 线程池已经被关闭;2. 任务队列已满且maximumPoolSizes已满;
无论哪种情况,都会调用RejectedExecutionHandler的rejectedExecution方法。预定义了四种处理策略:
- AbortPolicy:默认测策略,抛出RejectedExecutionException运行时异常,阻止系统正常工作;
- CallerRunsPolicy:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;
- DiscardOledestPolicy:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;
- DiscardPolicy:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;
我们可以自己定义RejectedExecutionHandler,以适应特殊的容量和队列策略场景中。
Hook methods 钩子方法
ThreadPoolExecutor为提供了每个任务执行前后提供了钩子方法,重写beforeExecute(Thread,Runnable)和afterExecute(Runnable,Throwable)方法来操纵执行环境; 例如,重新初始化ThreadLocals,收集统计信息或记录日志等。此外,terminated()在Executor完全终止后需要完成后会被调用,可以重写此方法,以执行任殊处理。注意:如果hook或回调方法抛出异常,内部的任务线程将会失败并结束
Queue maintenance 维护队列
getQueue()方法可以访问任务队列,一般用于监控和调试。绝不建议将这个方法用于其他目的。当在大量的队列任务被取消时,remove()和purge()方法可用于回收空间。