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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

线程的创建与销毁

线程池的基本大小(Core Pool Size)最大大小(Maximum Pool Size)以及存活时间等因素共同负责线程的创建与销毁。

  • 基本大小也就是线程池的目标大小,即在没有任务执行时线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程

  • 线程池的最大大小表示可同时活动的线程数量的上限

  • 如果某个线程的空闲时间超过了存活时间,那么将被标记为可回收的,并且当线程池的当前大小超过了基本大小时,这个线程将被终止。

在创建 ThreadPoolExecutor 初期,线程并不会立即启动,而是等到有任务提交时才会启动,除非调用 prestartAllCoreThreads。

管理队列任务

ThreadPoolExecutor 允许提供一个 BlockingQueue 来保存等待执行的任务。基本的任务排队方法有 3 种:无界队列有界队列同步移交

一种稳妥的资源管理策略是使用有界队列,例如 ArrayBlockingQueue有界的 LinkedBlockingQueuePriorityBlockingQueue有界队列有助于避免资源耗尽的情况发生。

对于非常大的或者无界的线程池,可以通过使用 SynchronousQueue 来避免任务排队,直接将任务从生产者移交给工作者线程。

饱和策略

当有界队列被填满后,饱和策略开始发挥作用。(如果某个任务被提交到一个已被关闭的 Executor 时,也会用到饱和策略。)ThreadPoolExecutor 的饱和策略可以通过调用 setRejectedExecutionHandler 来修改。

JDK 提供了几种不同的饱和策略:AbortPolicyCallerRunsPolicyDiscardPolicy DiscardOldestPolicy:

  • 中止(Abort)”策略是默认的饱和策略该策略将抛出未检查的 RejectedExecutionException

  • 当新提交的任务无法保存到队列中等待执行时,“抛弃(Discard)”策略会悄悄抛弃该任务

  • 抛弃最旧的(Discard-Oldest)”策略则会抛弃下一个将被执行的任务然后尝试重新提交新的任务。如果工作队列是一个优先队列,那么“抛弃最旧的”策略将导致抛弃优先级最高的任务

  • 调用者运行(Caller-Runs)”策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。它不会在线程池的某个线程中执行新提交的任务,而是在调用 execute 方法的线程中执行该任务。

线程工厂

每当线程池需要创建一个线程时,都是通过线程工厂方法来完成的。默认的线程工厂方法将创建一个新的、非守护的线程,并且不包含特殊的配置信息。

ThreadFactory 中只定义了一个方法 newThread,每当线程池需要创建一个新线程时都会调用这个方法。

扩展 ThreadPoolExecutor

ThreadPoolExecutor 是可扩展的,它提供了几个可以在子类化中改写的方法:beforeExecuteafterExecute terminated,这些方法可以用于扩展 ThreadPoolExecutor 的行为。

  • 无论任务是从 run 中正常返回,还是抛出一个异常而返回,afterExecute 都会被调用。(如果任务在完成后带有一个 Error,那么就不会调用 afterExecute。)

  • 如果 beforeExecute 抛出一个 RuntimeException,那么任务将不被执行,并且 afterExecute 也不会被调用。

  • 在线程池完成关闭操作时调用 terminated,也就是在所有任务都已经完成并且所有工作者线程也已经关闭后。terminated 可以用来释放 Executor 在其生命周期里分配的各种资源,此外还可以执行发送通知、记录日志或者收集 finalize 统计信息等操作。

最后更新于