线程池:Executor框架源码解析(上)

Java内部提供了线程池Executor框架,其功能比之前笔记中自实现的简易线程池更加全面。

1.整体架构

Executor框架的整体架构如下,
image

接口/类说明
Executor该接口只有一个execute方法,目的是将任务提交和执行解耦
ExecutorService该接口对Executor接口进行了拓展,定义了对线程池中线程的管理方法
AbstractExecutorService对ExecutorService接口中部分方法的实现
ThreadPoolExecutor线程池的最终实现,实现了线程池工作的完整机制,是整个框架的重点
ForkJoinPool实现了Fork/Join模式的线程池
ScheduleExecutorService对ExecutorService进行了拓展,定义了延迟执行和周期执行任务的方法
ScheduleThreadExecutorService在继承ThreadPoolExecutorService的基础上对ScheduleExecutorService接口进行了实现

2.源码解析

Executor接口

该接口只有一个方法executor,传入参数是 Runnable类型,

public interface Executor {
    void execute(Runnable command);
}

ExecuorService接口

该接口对 Executor接口进行了拓展,定义了线程池管理和更多执行任务的方法,
image
重要的方法说明,

方法说明
shutdown终止ExecutorService,不再接收和执行新的任务,已经执行的任务会被执行完
shutdownNow立刻终止ExecutorService,不再接收和执行新的任务,已经执行的任务也会终止(但不保证能够被终止)
submitexecute方法的拓展,返回一个 Future类对象
invokeAll执行一组任务,所有任务都返回或者 timeout 的时候,invokeAll 方法返回执行结果列表。该方法一旦返回结果,没有完成的任务则被取消
invokeAny执行一组任务,任意一个任务有返回时,invokeAny 返回该任务的执行结果。其余没有完成的任务则被取消。

AbstractExecutorService类

该抽象类封装了 Executor的很多通用功能,最重要的两个方法是newTaskForsubmit方法。

1)构建任务—newTaskFor方法

该方法的实现有两种形式,分别针对 Runnable和 Callable接口的实现类,

// 传入参数为 Runnable
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

// 传入参数为 Callable
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

返回值的类型是 FutureTask类对象,属于 RunnableFuture接口的实现类。该类的具体实现参见笔记,可简单理解为将Runnable和Callable统一封装为Callable对象。

2)构建任务并执行—submit方法

该方法的实现同样依据传入的参数是 Runnable和Callable分为两种,

// 提交无返回值的任务
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    // ftask 其实是 FutureTask
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

// 提交有返回值的任务
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    // ftask 其实是 FutureTask
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

对该抽象类进行如下说明,

  1. AbstractExecutorService类最主要的是实现了submit方法,用于提交和执行任务。其内部调用了自身实现的newTaskFor方法和Executor接口定义的execute方法。
    newTaskFor方法中构建的任务是FutureTask类对象,属于RunnableFuture类的子类。RunnableFuture实现类Runnable接口和Future接口,所以可以被传入 Executor接口的execute方法
  2. execute方法的具体实现是在该类的子类 ThreadPoolExecutor中

ThreadPoolExecutor类*

1)类注释

ThreadPoolExecutor类的注释很多,关键注释如下,

  1. 线程池解决两个问题,通过减少任务间的调度开销提高大量任务时的执行性能(主要是线程被重复使用);提供一种方式管理线程的消费,维护基本数据并对已完成任务进行统计
  2. Executors 为常用的场景设定了可直接初始化线程池的方法,比如
    Executors#newCachedThreadPool 无界的线程池,并且可以自动回收;
    Executors#newFixedThreadPool 固定大小线程池;
    Executors#newSingleThreadExecutor 单个线程的线程池等
  3. 为了在各种上下文中使用线程池,线程池提供可供扩展的参数设置,
    coreSize:当新任务提交时,发现运行的线程数小于 coreSize,一个新的线程将被创建,即使这时候其它工作线程是空闲的,可以通过 getCorePoolSize 方法获得 coreSize;
    maxSize:当任务提交时,coreSize < 运行线程数 <= maxSize,但队列没有满时,任务提交到队列中,如果队列满了,在 maxSize 允许的范围内新建线程
  4. 默认的,core threads 需要到任务提交后才创建的,但可以分别使用 prestartCoreThreadprestartAllCoreThreads 两个方法来提前创建一个、所有的 core threads
  5. 新的线程被默认 ThreadFactory 创建时,优先级会被限制成 NORM_PRIORITY,默认会被设置成非守护线程,这个和新建线程的继承是不同
  6. Keep-alive times 参数的作用,
    i. 如果当前线程池中有超过 coreSize 的线程
    ii. 并且线程空闲的时间超过 keepAliveTime,当前线程就会被回收,这样可以避免线程没有被使用时的资源浪费
  7. 如果设置 allowCoreThreadTimeOut 为 ture 的话,core thread 空闲时间超过 keepAliveTime 的话,也会被回收
  8. 线程池新建时,有多种任务队列可供选择,
    i. SynchronousQueue,为了避免任务被拒绝,要求线程池的 maxSize 无界,缺点是当任务提交的速度超过消费的速度时,可能出现无限制的线程增长;
    ii. LinkedBlockingQueue,无界队列,未消费的任务可以在队列中等待;
    iii. ArrayBlockingQueue,有界队列,可以防止资源被耗尽
  9. 在 Executor 已经关闭或对最大线程和最大队列都使用饱和时,可以使用 RejectedExecutionHandler 类进行异常捕捉,有如下四种处理策略:ThreadPoolExecutor.AbortPolicy、ThreadPoolExecutor.DiscardPolicy、ThreadPoolExecutor.CallerRunsPolicy、ThreadPoolExecutor.DiscardOldestPolicy
    10.线程池提供了很多可供扩展的钩子函数
    i. 提供在每个任务执行之前beforeExecute和执行之后 afterExecute 的钩子方法,主要用于操作执行环境,比如初始化 ThreadLocals、收集统计数据、添加日志条目等;
    ii. 如果在执行器执行完成之后想干一些事情,可以实现 terminated 方法
    如果钩子方法执行时发生异常,工作线程可能会失败并立即终止

2)ThreadPoolExecutor重要属性

用户可控制的属性都是 volatile 关键字修饰的,

属性说明
private final AtomicInteger ctl线程池状态控制字段由两部分组成,工作线程数wc和线程池状态rs具体见表格下方说明
volatile long completedTasks已完成任务的计数
private long completedTaskCount已经完成的任务数
private int largestPoolSize线程池最大容量
private volatile ThreadFactory threadFactory可以使用 threadFactory 创建 线程
private volatile RejectedExecutionHandler handler饱和或者运行中拒绝任务的 handler 处理类
private volatile long keepAliveTime线程存活时间设置
private volatile boolean allowCoreThreadTimeOut设置 true 的话,核心线程空闲 keepAliveTime 时间后,也会被回收
private volatile int corePoolSize核心线程数目
private volatile int maximumPoolSize用户设定的线程池最大线程数
private static final RejectedExecutionHandler defaultHandler默认的拒绝策略,默认是 AbortPolicy类对象
private final BlockingQueue workQueue任务队列,利用队列的阻塞的特性
private final HashSet workers包含线程池中所有的工作线程,元素是Worker类对象
private final ReentrantLock mainLock可重入锁对象,控制对workers队列的访问
private final Condition termination可重入锁对象mainLock的条件队列,控制对workers队列的访问
工作线程数—workerCount

相关属性和方法,

private static final int COUNT_BITS = Integer.SIZE - 3;			// 29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;	// =(2^29)-1=536870911

private static int workerCountOf(int c) { 
	return c & CAPACITY; 
}
线程池状态—runState

线程池相关状态,

private static final int RUNNING    = -1 << COUNT_BITS;		//-536870912
private static final int SHUTDOWN   =  0 << COUNT_BITS;		//0
private static final int STOP       =  1 << COUNT_BITS;		//-536870912
private static final int TIDYING    =  2 << COUNT_BITS;		//1073741824
private static final int TERMINATED =  3 << COUNT_BITS;		//1610612736

private static int runStateOf(int c) { 
	return c & ~CAPACITY; 
}

线程池状态转换,
image

状态说明
RUNNING(-536870912)接受新任务或者处理队列里的任务。
SHUTDOWN(0)不接受新任务,但仍在处理已经在队列里面的任务。执行 shutdown()finalize()方法后从 RUNNING到 SHUTDOWN状态
STOP(-536870912)不接受新任务,也不处理队列中的任务,对正在执行的任务进行中断。执行 shutdownNow()方法后从 RUNNING到 STOP状态
TIDYING(1073741824)所以任务都被中断,workerCount 是 0,整理状态。SHUTDOWN/STOP -> TIDYING -> workerCount=0
TERMINATED(1610612736)执行terminated() 方法执行完成时从 TIDYING到 TERMINATED
线程池状态控制字段—ctl

ctl变量由工作线程数和线程池状态共同构成,该变量是一个 AtomicInteger 的类,就是让保存的 int 变量的更新都是原子操作,保证线程安全。

private static int ctlOf(int rs, int wc) { 
	return rs | wc; 
}

ctlOf方法就是组合运行状态和工作线程数量,该方法是通过按位或的方式来实现的。这里把一个 int 变量拆成两部分来用。前面3位用来表示状态,后面29位用来表示工程线程数量。所以,工作线程数量最大不能超过 2 29 − 1 2^{29}-1 2291
image

该变量一会儿用来获取线程数量workerCountOf方法,一会儿又用来判断线程池是否处于运行状态isRunning方法。这两个方法源码如下,

// 这两个方法的参数c就是当前线程池 ctl的值
private static int workerCountOf(int c) { 
	return c & CAPACITY;
}

private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

第一个方法中又多出一个CAPACITY常量,

private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

CAPACITY的值为 000111...111 000111...111 000111...111(共计32位)。

workerCountOf方法中按位与之后去掉了前面3位,保留了后面29位。所以,拿到的就是工作线程的数量。

isRunning 方法中,直接拿 ctl 的值和 SHUTDOWN 作比较。首先明确在 RUNNING 状态下,ctl 的值是什么样的,

  • 初始状态,ctl 的值是 11100000... … 00000000 11100000 ... … 00000000 11100000...00000000,表示 RUNNING 状态,和0个工作线程。
  • 每创建一个新线程,都把 ctl 加1。当有5个工作线程时,ctl 的值是 11100000... … 00000101 11100000 ... … 00000101 11100000...00000101

RUNNING状态下,ctl 始终是负值,而 SHUTDOWN 是0,所以可以通过直接比较 ctl 的值来确定状态。

3)构造方法

一般并不直接通过ThreadPoolExecutor的构造方法创建线程池,而是通过Executors类提供的工厂函数进行创建,

Executors.newFixedThreadPool(10)

通过 Executors 的工厂方法来创建ThreadPoolExecutor对象。Executor 提供了多种工厂方法创建线程池。其实根本是调用 ThreadPoolExecutor 构造方法时传入参数不同。以newFixedThreadPool 方法为例,源代码如下,

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

底层调用的 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.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

参数说明,

参数说明
corePoolSize线程池核心线程数量,即最小线程数量。不设置allowCoreThreadTimeout时,核心线程一直存活
maximumPoolSize线程池最大线程数量,受限于CAPACITY的大小
workQueue一个阻塞队列,用于保存线程池要执行的所有任务
threadFactory规范了生成的线程,避免了手动调用new Thread()时创建出来的线程的差异,创建得到的线程具有连续的线程号码

构造过程中没有启动任何线程,避免对资源的浪费。

4)内部封装线程—Worker类

Worker封装了线程,是线程池中的工作单元。该类继承了 AQS,同时实现了 Runnable。

类属性

该类的属性如下,

// 该worker对象中封装的线程,如果是 null说明ThreadFactory构建线程失败
final Thread thread;
// 需要执行的任务
Runnable firstTask;
// 记录每个线程完成的任务数目
volatile long completedTasks
构造方法

构造方法如下,

Worker(Runnable firstTask) {
    setState(-1); 
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}

创建 thread 时,Worker 对象将自己作为 Runnable 的实现传入了 thread 中。线程池在执行时调用的 t.start(),实际上运行的是 t 所属 Worker类的run方法。

run方法

worker 的 run 方法如下,

public void run() {
    runWorker(this);
}

上面的代码中,runWorker方法是外部类 ThreadPoolExecutor的方法,之后会讲解。

对Worker类的理解
  1. Worker类像是任务代理,是线程池中最小的执行单位,对线程池中的工作线程进行了良好的封装
  2. Worker 本身也实现了 AQS,所以其本身也是一个锁,其在执行任务的时候,会锁住自己,任务执行完成之后,会释放自己
  3. 在 Worker 初始化时 this.thread = getThreadFactory ().newThread (this) 这行代码比较关键。它把当前 Worker 作为线程的构造器入参,在后续的实现中会发现这样的代码:Thread t = w.thread;t.start(),此时的 w 是 Worker 的引用,此处 t.start 实际上执行的就是 Worker 的 run 方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值