Executor框架简介
Executor框架的两级调度模型
Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。被称为两级调度模型。
Executor框架的结构与成员
- Executor框架的结构
Executor框架主要由3大部分组件组成:
- 任务: 包括被执行任务需要实现的接口:Runnable接口或Callable接口
- 任务的执行。包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。Executor框架有两个关键实现类实现了ExecutorService接口,ThreadPoolExecutor和ScheduledThreadPoolExecutor
下面是这些类和接口的简介: - Executor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离
- ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务
- ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令,或者定期执行命令。功能类似 于Timer,但比Timer更灵活,功能更强大
- Future接口和实现Future接口的FutureTask类,代表异步计算的结果
Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行
ThreadPoolExector详解
关于ThreadPoolExector的4个组件构成已经在java线程池实现原理中介绍过,在此不再重复,下面介绍通过Exectors框架的工具类Exectors可以创建的3种类型的ThreadPoolExector:
- FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。
当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。这里把keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。
FixedThreadPool使用无界队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为Integer.MAX_VALUE)。使用无界队列作为工作队列会对线程池带来如下影响。
- 当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中线程数不会超过corePoolSize。
- 由于1,使用无界队列时maximumPoolSize将是一个无效参数。
- 由于1和2,使用无界队列时keepAliveTime将是一个无效参数。
- 由于使用无界队列,运行中的FixedThreadPool(未执行shutDown()或shutDownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)
-
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使用无界队列作为工作队列对线程池带来的影响与FixedThreadPool相同。
-
CachedThreadPool
CachedThreadPool是一个会根据需要创建新线程的线程池。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool的corePoolSize被设置成0,即corePoolSize为空;maximumPoolSize被设置为Integer.MAX_VALUE,即maximumPool是无界的。这里把keepAliveTime设置为60L,意味着CachedThreadPool中的空闲线程等待任务的最长时间为60s,空闲线程超过60s后将会被终止。
CachedThreadPool使用没有没有容量的SynchronousQueue作为线程池工作队列,但CachedThreadPool的maximumPool是无界的。这意味着,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool就会不断地创建新任务。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。
In The End
对比
corePoolSize | maximumPoolSize | workQueue | keepAliveTime | 特点 | |
---|---|---|---|---|---|
FixedThreadPool | nTread | nTread | LinkedBlockingQueue | 0 | 不会拒绝任务 |
SingleThreadExecutor | 1 | 1 | LinkedBlockingQueue | 0 | 只有单个worker线程 |
CachedThreadPool | 0 | Integer.MAX_VALUE | SynchronousQueue | 60 | 使用阻塞队列; maximumPoolSize无界 |
适用场景
FixedThreadPool:由于FixedThreadPool使用无界队列,所以不会拒绝任务。因此合理设置corePoolSize数量配合任务队列,适用于负载比较重的服务器。
SingleThreadExecutor:SingleThreadExecutor只有一个worker线程,所以提交到SingleThreadExecutor的任务会保证顺序的执行,因此SingleThreadExecutor适用于需要保证顺序的执行任务,并且整个生命周期不会有多个线程的场景。
CachedThreadPool:CachedThreadPool时大小无界的线程池,因此适用于执行很多短期异步任务的小程序,或者是负载较轻的服务器。
网友评论