概述
- Executor线程执行器框架是在jDK1.5提供的,设计的主要目的是实现Runnable任务的提交和执行分离:任务提交主要是指在应用代码中实现Runnable接口定义业务相关的一个任务,然后交给一个Thread线程对象来执行;任务的执行是指Thread线程执行该任务。有了Executor线程执行器之后,我们只需要实现Runnalbe接口定义自己的任务即可,不需要显示创建Thread对象来执行,而是交给Executor,Executor会负责调度线程来执行该任务,具体为:
- 在Executor内部的线程池调度一个空闲的线程来执行该任务,或者如果没有空闲线程,则将该任务放到内部的一个队列排队等待,或者如果没有空闲线程,队列也没有空闲空间存放了,则使用相应的拒绝策略来拒绝执行这个任务。
- 由于Executor是异步执行任务的,对每次的任务提交可以返回一个Future对象,可以通过该Future对象来阻塞等待该任务的执行结果,或者通过该Future对象来取消该任务的执行。
- 除了实现任务提交和任务执行分离来实现自动化线程调度执行,简化编程复杂度,Executor线程执行器还包括以下优点:
- 通过线程池机制,实现线程复用,避免频繁的线程创建和销毁,提高了在执行大量任务时的性能,避免之前那种每个任务显式创建一个Thread对象来执行;
- 通过设置线程池和内部队列的大小,可以根据当前系统资源情况,灵活控制系统的资源消耗,避免创建过多线程或者队列存放过多任务,造成系统资源耗尽,如内存,CPU资源,导致系统宕机;
- 提供了任务执行情况的统计功能,如该Executor实例当前一共执行了多少任务。
ThreadPoolExecutor:线程池定义
- Executor接口的核心实现类为ThreadPoolExecutor,在ThredPoolExector中主要维护了一个线程池,一个线程安全的阻塞队列来进行任务的管理和执行,同时在类设计层面,ThreadPoolExecutor提供以下几个参数来对内部的线程池,队列等进行控制:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
- corePoolSize:线程池核心大小,即任务需要排队之前的线程池的最大大小,如果corePoolSize个线程全部繁忙,则新来的任务需要排队了;
- maximumPoolSize:线程池最大大小,即当corePoolSize个线程全部繁忙,等待队列workQueue也满了,则会继续新建线程来执行任务,直到总的线程数量达到maximumPoolSize。所以如果workQueue使用的是无界队列,则该参数无效,因为队列永远不会满;
- keepAliveTime:超过corePoolSize的线程,如果空闲超过keepAliveTime时间,则需要被销毁回收,默认corePoolSize范围内的线程一旦创建,即使空闲也不会回收的。在内部实现中,由于每个线程在空闲时是在工作队列workQueue中阻塞等待任务到来的,故该keepAliveTime是设置在 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),即阻塞keepAliveTime时间之后,如果超时,则需要销毁该线程了,具体后面分析;
- workQueue:当前没有空闲线程时,任务的等待队列;
- threadFactory:线程池的线程创建工厂类;
- RejectedExecutionHandler:任务拒绝策略,即线程池没有空闲线程且无法继续创建线程,队列也排满等待执行的任务时,新来的任务的拒绝策略。
- 以下主要基于ThreadPoolExecutor来对Executor线程执行器进行分析。
构造函数
- ThreadPoolExecutor的构造函数主要是对以上参数进行赋值,具体的线程池中的线程是延迟初始化的,即在有任务提交的时候才开始创建线程:

工作线程和线程池
- 工作线程池主要通过一个HashSet来实现,使用HashSet而不会存在线程安全问题是因为在ThreadPoolExecutor内部对HashSet中节点的增删,即线程节点的增删,都是需要通过一个ReentrantLock来加锁,定义如下:

- 工作线程主要是通过定义一个Worker类来实现:继承于AQS,实现了Runnable接口,继承AQS的主要目的是使用AQS提供的互斥锁,即state=1,来表明当前worker是否在处理任务还是空闲,实现Runnable接口的主要目的是:将该worker对象自身作为一个task放到Worker内部的线程thread执行,在run方法中定义该工作线程的工作逻辑,主要是调用runWorker(this)方法,具体在任务执行部分分析runWorker。
- 内部包含了一个Thread对象,这个是实际执行任务的线程,同时包含一个Runable对象firstTask,由于是延迟创建工作线程的,所以这个是第一个触发创建这个工作线程的任务。

任务的提交
- 任务的提交主要在ThreadPoolExecutor的execute方法定义,其中execute为void方法,没有返回值;如果提交任务,需要获取一个Future对象来跟踪这个任务的执行,获取执行结果,则需要使用submit方法。在内部实现中submit也是调用了execute来提交任务到线程池。
- execute定义:具体看代码注释,其中完成了对Executor框架设计中对corePoolSize,等待队列,maximumPoolSize,拒绝策略相关语义的实现,即对一个新任务的提交:
- 当线程池线程数量少于corePoolSize时,则创建一个新的线程来执行这个任务,不管当前线程池是否存在空闲线程;
- 当线程池线程数量达到corePoolSize时,则将该任务放到等待队列中,由于线程池中的空闲线程从这个队列取出然后执行;其中workQueue的offer方法为非阻塞,成功则返回true,否则返回false;
- 当等待队列workQueue也满了,当线程池线程数量少于maximumPoolSize时,则继续创建新线程执行这个任务,如果线程数量超过了maximumPoolSize,则此时说明存在太大任务等待执行了,则调用reject方法,根据具体的拒绝策略处理这个任务,默认为AbortPolicy,即在execute方法抛异常,所以如果可能存在这种情况,则一般需要在应用代码中捕获该异常。

- addWorker延迟创建工作线程实现如下:首先在第一个自旋中通过比较当前线程池线程数量,corePoolSize,maximumPoolSize来确定是否可以继续创建线程;如果可以则创建Worker工作线程,并在加锁mainLock的包含下,将该工作线程添加到线程池workers中。最后调用该工作线程内部的线程thread的start的方法,开始这个工作线程的执行。

任务的执行
- 每个工作线程Worker主要是在run方法中调用runWorker来定义工作线程的具体工作逻辑,即执行创建该worker时提交的任务和从任务等待队列workQueue获取提交的任务并执行:主要是在while循环中调用getTask方法从任务等待队列获取任务,其中getTask为阻塞执行的,即如果等待队列没有任务,则阻塞等待:

- getTask:从任务等待队列workQueue阻塞等待获取任务。同时也在这里实现了超过corePoolSize的线程的超时清理控制,即如果超过了corePoolSize的线程,则调用了workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)法,即阻塞keepAliveTime时间,如果还是没有任务,则返回null,则在runWorker中退出while循环,然后关闭这个工作线程。

任务的执行结果
- 如果需要获取任务的执行结果,则需要通过ThreadPoolExecutor的submit方法来提交任务,这个方法会返回一个Future接口的实现类对象,具体为FutureTask。submit方法在ThreadPoolExecutor的基类,即抽象类AbstractExecutorService中定义:内部是将task包装成了FutureTask,然后通过execute方法来提交到线程池。

任务的拒绝
- 当线程池无法继续创建线程,任务等待队列都满了时,则对于新提交的任务,则通过reject方法来调用对应的拒绝策略来处理。
- ThreadPoolExecutor提供了四种拒绝策略,分别为(1)抛Abort异常,(2)在线程池主线程中直接执行该任务,(3)默默丢弃不做任务操作,(4)从任务等待队列移除等待最久的任务,即队列头对应的任务,默认为(1)抛Abort异常:

线程池的关闭
- 线程池的关闭主要包括平滑关闭和暴力关闭两种,内部主要是通过runState状态变量来做控制。
- shutdown:平滑关闭,停止新任务的提交,等待正在排队的任务和正在执行的任务执行完成:

- shutdownNow:暴力关闭,停止新任务的提交,中断正在执行任务的线程,停止排队任务的执行和尝试停止正在执行任务的执行。
