ThreadPoolExecutor简介
ThreadPoolExecutor是线程池类。对于线程池,可以通俗的将它理解为”存放一定数量线程的一个线程集合。
Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类,主要由下列4个组件构成。
- ·corePool:核心线程池的大小。
- ·maximumPool:最大线程池的大小。
- ·BlockingQueue:用来暂时保存任务的工作队列。
- ·RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和 时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler。
·通过Executor框架的工具类Executors,可以创建3种类型的ThreadPoolExecutor。
·FixedThreadPool。
·SingleThreadExecutor。
·CachedThreadPool。
ThreadPoolExecutor数据结构
ThreadPoolExecutor的数据结构如下图所示:
各个数据在ThreadPoolExecutor.java中的定义如下:
// 阻塞队列。 private final BlockingQueue<Runnable> workQueue; // 互斥锁 private final ReentrantLock mainLock = new ReentrantLock(); // 线程集合。一个Worker对应一个线程。 private final HashSet<Worker> workers = new HashSet<Worker>(); // “终止条件”,与“mainLock”绑定。 private final Condition termination = mainLock.newCondition(); // 线程池中线程数量曾经达到过的最大值。 private int largestPoolSize; // 已完成任务数量 private long completedTaskCount; // ThreadFactory对象,用于创建线程。 private volatile ThreadFactory threadFactory; // 拒绝策略的处理句柄。 private volatile RejectedExecutionHandler handler; // 保持线程存活时间。 private volatile long keepAliveTime; private volatile boolean allowCoreThreadTimeOut; // 核心池大小 private volatile int corePoolSize; // 最大池大小 private volatile int maximumPoolSize;
1. workers
workers是HashSet<Work>类型,即它是一个Worker集合。而一个Worker对应一个线程,也就是说线程池通过workers包含了”一个线程集合”。当Worker对应的线程池启动时,它会执行线程池中的任务;当执行完一个任务后,它会从线程池的阻塞队列中取出一个阻塞的任务来继续运行。
wokers的作用是,线程池通过它实现了”允许多个线程同时运行”。
2. workQueue
workQueue是BlockingQueue类型,即它是一个阻塞队列。当线程池中的线程数超过它的容量的时候,线程会进入阻塞队列进行阻塞等待。
通过workQueue,线程池实现了阻塞功能。
3. mainLock
mainLock是互斥锁,通过mainLock实现了对线程池的互斥访问。
4. corePoolSize和maximumPoolSize
corePoolSize是”核心池大小“,maximumPoolSize是”最大池大小“。它们的作用是调整”线程池中实际运行的线程的数量”。
例如,当新任务提交给线程池时(通过execute方法)。
— 如果此时,线程池中运行的线程数量< corePoolSize,则创建新线程来处理请求。
— 如果此时,线程池中运行的线程数量> corePoolSize,但是却< maximumPoolSize;则仅当阻塞队列满时才创建新线程。
如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心池大小和最大池大小的值是在创建线程池设置的;但是,也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。
5. poolSize
poolSize是当前线程池的实际大小,即线程池中任务的数量。
6. allowCoreThreadTimeOut和keepAliveTime
allowCoreThreadTimeOut表示是否允许”线程在空闲状态时,仍然能够存活”;而keepAliveTime是当线程池处于空闲状态的时候,超过keepAliveTime时间之后,空闲的线程会被终止。
7. threadFactory
threadFactory是ThreadFactory对象。它是一个线程工厂类,”线程池通过ThreadFactory创建线程”。
8. handler
handler是RejectedExecutionHandler类型。它是“线程池拒绝策略”的句柄,也就是说”当某任务添加到线程池中,而线程池拒绝该任务时,线程池会通过handler进行相应的处理”。
FixedThreadPool
对图10-4的说明如下。
1)如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。
2)在线程池完成预热之后(当前运行的线程数等于corePoolSize),将任务加入 LinkedBlockingQueue。
3)线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行。
FixedThreadPool使用无界队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为 Integer.MAX_VALUE)。
使用无界队列作为工作队列会对线程池带来如下影响。
1)当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中 的线程数不会超过corePoolSize。
2)由于1,使用无界队列时maximumPoolSize将是一个无效参数。
3)由于1和2,使用无界队列时keepAliveTime将是一个无效参数。
4)由于使用无界队列,运行中的FixedThreadPool(未执行方法shutdown()或 shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)。
SingleThreadExecutor
SingleThreadExecutor是使用单个worker线程的Executor
SingleThreadExecutor的corePoolSize和maximumPoolSize被设置为1。其他参数与 FixedThreadPool相同。SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的工 作队列(队列的容量为Integer.MAX_VALUE)。SingleThreadExecutor使用无界队列作为工作队列 对线程池带来的影响与FixedThreadPool相同,这里就不赘述了。
对图10-5的说明如下。
1)如果当前运行的线程数少于corePoolSize(即线程池中无运行的线程),则创建一个新线
程来执行任务。
2)在线程池完成预热之后(当前线程池中有一个运行的线程),将任务加入LinkedBlockingQueue。
3)线程执行完1中的任务后,会在一个无限循环中反复从LinkedBlockingQueue获取任务来
执行。
CachedThreadPool
CachedThreadPool是一个会根据需要创建新线程的线程池。
CachedThreadPool的corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置为 Integer.MAX_VALUE,即maximumPool是无界的。这里把keepAliveTime设置为60L,意味着 CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被
终止。
FixedThreadPool和SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的 工作队列。CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但 CachedThreadPool的maximumPool是无界的。这意味着,如果主线程提交任务的速度高于 maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程。极端情况下, CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。
对图10-6的说明如下。
1)首先执行SynchronousQueue.offer(Runnable task)。如果当前maximumPool中有空闲线程 正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行 offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方 法执行完成;否则执行下面的步骤2)。
2)当初始maximumPool为空,或者maximumPool中当前没有空闲线程时,将没有线程执行 SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤1)将失 败。此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。
3)在步骤2)中新创建的线程将任务执行完后,会执行 SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线
程最多在SynchronousQueue中等待60秒钟。如果60秒钟内主线程提交了一个新任务(主线程执 行步骤1)),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止。由于 空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。
前面提到过,SynchronousQueue是一个没有容量的阻塞队列。每个插入操作必须等待另一 个线程的对应移除操作,反之亦然。CachedThreadPool使用SynchronousQueue,把主线程提交的 任务传递给空闲线程执行。CachedThreadPool中任务传递的示意图如图10-7所示。
参考文献:
《Java并发编程艺术》
https://www.cnblogs.com/skywang12345/p/3509941.html