由继承关系可以看出线程池的顶级接口是Executor,但正如我之前在接口和抽象类中所说的,Executor并不是描述一个线程池,它只提供了一个 void execute(Runnable command)方法,可以看出来是执行任务的入口。
真正的线程池接口描述是ExecutorService,里面已经提供了shutdown(),submit(Callable task)等核心方法,而真正的线程池实现则是下面要重点分析的ThreadPoolExecutor类
/**
* corePoolSize 线程池的基本大小,即没有任务时,保持的线程数量,通常情况下这里的线程数量不会超时,除非设置allowCoreThreadTimeOut
* maximumPoolSize 线程池所能容纳的最大线程数量
* keepAliveTime 如果超出核心线程大小部分的线程空闲时间大于这个值就会超时而退出,核心线程是否会超时退出取allowCoreThreadTimeOut * 的设置
* TimeUnit 时间单位(比如毫秒TimeUnit.MILLISECONDS,秒)
* BlockingQueue 任务等待队列,当前等待分配线程的任务
* threadFactory 生成线程工厂
* handler 当任务拒绝处理时的处理策略,有以下几种
* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
* ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
*/
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;
}
有了以上对构造方法的分析,就很好理解常见的几种创建线程池的方式,也是jdk官方推荐的创建线程池的方式。
// 创建一个单线程的线程池
Executors.newSingleThreadExecutor();
// 创建一个可自动回收线程的线程池
Executors.newCachedThreadPool();
// 创建一个固定核心线程数线程池
Executors.newFixedThreadPool(int nThreads);
下面对这几个方法做个简单分析
public static ExecutorService newSingleThreadExecutor() {
// 这里用一个静态内部类对线程池对象进行了包装,暂时先不管。它直接调用了ThreadPoolExecutor的构造方法,核心线程数和最大线程数都给了1,显然这个线程池
// 只能容纳一个线程处于运行状态,keepalivetime是0,因为最大线程和核心线程一致,不会有超出核心线程的空闲线程被回收,除非allowcorethreadtimeout设置true,否则
// 核心线程不会被回收,值得注意的是这里的任务队列LinkedBlockingQueue,这个任务队列有个特点,它是无界的。
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
// 这里可以看到给定的线程数int nThreads被赋值给corePoolSize和maximumPoolSize,和SingleThreadExecutor一样这两个参数相等,只不过值不再
// 是1,而是给定的值,可想而知该线程池也会保持最大线程数,除非有线程执行完毕或者因错误退出,否则不会被回收
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
// 该方法是创建一个可以自动回收的线程的线程池,根据内部的实现,我们可以轻易发现,它对容纳线程数量几乎没有限制,直接是Integer.MAX_VALUE,而自动回收是
// 怎么实现的呢?核心线程为0,keepalivetime为60秒,意味着只要线程处于空闲状态60秒就会被自动回收
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}