ThreadPoolExecutor 源码阅读与分析

159 阅读13分钟

前序

引入

经常使用类似的语句创建线程池

ExecutorService executorService = Executors.newFixedThreadPool(100);

那么它都做了那些事?

构建线程池的顶级方法

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

模板方法的一个实现:

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue) {
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
		 Executors.defaultThreadFactory(), defaultHandler);
}

创建实例的具体方法:

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;

	String name = Objects.toIdentityString(this);
	this.container = SharedThreadContainer.create(name);
}

想要知道这些参数的作用,我们需要查看 execute 方法都做了什么?

顶级的执行方法是 java.util.concurrent.Executor#execute:

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

在 ThreadPoolExecutor 中的实现:

 public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

提交线程—计算 WorkerCount

初步解读其中的实现:

int c = ctl.get();
workerCountOf(c);
addWorker(command, true);

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }

private static int workerCountOf(int c)  { return c & COUNT_MASK; }

private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// 0010 0000 0000 0000 0000 0000 0000 0000 (2) - 1 (10) = 高3位为0 低29位为1
private static final int COUNT_BITS = Integer.SIZE - 3 
// 32 - 3 = 29;
@Native public static final int SIZE = 32;


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

这里作补充 << 是左移计算符,int 类型分 8 个字节,在计算机组成课程里解释为二进制下的 高 16 位与低 16 位。 相应的 >> 则是带符号右移计算符,>>> 是无符号右移计算符。

举个例子:十进制的 12 (10) 在二进制下代表 0000 1100 (2),那么 12 << 1 = 0001 1000 (2) = 24 (10)

带着补充的例子计算得出:

RUNNING = -1 << 29 的结果是:1110 0000 0000 0000 0000 0000 0000 0000(2)

int c = ctl.get();
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }

计算 RUNNING | 0 的结果是:1110 0000 0000 0000 0000 0000 0000 0000(2)

workerCountOf(c);
private static int workerCountOf(int c)  { return c & COUNT_MASK; }
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// 0010 0000 0000 0000 0000 0000 0000 0000 (2) - 1 (10) = 高3位为0 低29位为1

计算 (RUNNING | 0) & 0001 1111 1111 1111 1111 1111 1111 1111 (2)的结果:

1110 0000 0000 0000 0000 0000 0000 0000(2)

0001 1111 1111 1111 1111 1111 1111 1111(2)

0000 0000 0000 0000 0000 0000 0000 0000 (2)= 0 (10)

至此我们终于算出了此时正在工作的线程数为 0;那么如果 ctlOf(RUNNING, 1) 工作线程数量为 1 呢?

我们来推演一下此时运行以下代码:

int c = ctl.get();
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }

int threadCount =  workerCountOf(c);
private static int workerCountOf(int c)  { return c & COUNT_MASK; }
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

的结果 threadCount 是多少?

(1)RUNNING | 1 的结果:

1110 0000 0000 0000 0000 0000 0000 0000(2)

0000 0000 0000 0000 0000 0000 0000 0001(2)

1110 0000 0000 0000 0000 0000 0000 0001(2)

(2)(RUNNING | 1) & 0001 1111 1111 1111 1111 1111 1111 1111 (2)的结果:

1110 0000 0000 0000 0000 0000 0000 0001(2)

0001 1111 1111 1111 1111 1111 1111 1111 (2)

0000 0000 0000 0000 0000 0000 0000 0001 (2)= 1(10)

我们算出当工作线程数为 1 时计算出来的数据是 1,因此源代码的作者就是线程状态与其数据都存在了 32 位的无符号数上。

提交线程—添加 Worker

初步观察其入口方法:

if (addWorker(command, true))
	return;

其具体的方法体:

 private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (int c = ctl.get();;) {
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;

            for (;;) {
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateAtLeast(c, SHUTDOWN))
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();

                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        workerAdded = true;
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    container.start(t);
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

接下来我们逐一分析上述方法体的代码

if (runStateAtLeast(c, SHUTDOWN)
	&& (runStateAtLeast(c, STOP)
		|| firstTask != null
		|| workQueue.isEmpty()))
	return false;

线程池的状态为停机或以上的非运行态且(已经停止 或 提交的任务不为空 或 工作队列为空)即完全可以停止的状态时,不允许提交 wroker 进入线程池运行。

for (;;) {
	if (workerCountOf(c)
		>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
		return false;
	if (compareAndIncrementWorkerCount(c))
		break retry;
	c = ctl.get();  // Re-read ctl
	if (runStateAtLeast(c, SHUTDOWN))
		continue retry;
	// else CAS failed due to workerCount change; retry inner loop
}

正在工作的线程数大于了核心线程数或能容纳的最大线程数时,不允许提交 wroker。

比较添加工作线程的计数,比较成功时跳出循环。

如果添加工作线程计数失败,重新测试线程池的状态,继续循环。

int c = ctl.get();

if (isRunning(c) ||
	(runStateLessThan(c, STOP) && firstTask == null)) {
	if (t.getState() != Thread.State.NEW)
		throw new IllegalThreadStateException();
	workers.add(w);
	workerAdded = true;
	int s = workers.size();
	if (s > largestPoolSize)
		largestPoolSize = s;
}

进到这里,其实工作线程的计数已经添加完毕,如果此时线程池依旧运行态或者命令无需执行。

创建 worker 并启动线程。

提交线程—推入队列

if (isRunning(c) && workQueue.offer(command)) {
	int recheck = ctl.get();
	if (! isRunning(recheck) && remove(command))
		reject(command);
	else if (workerCountOf(recheck) == 0)
		addWorker(null, false);
}else if (!addWorker(command, false))
	reject(command);

显然如果添加 wroker 失败了,但是线程池还在运行中,那么我们就将指令送入工作队列中。

如果推入队列后线程池正常运行,本次执行方法结束。

提交线程—拒绝处理

if (isRunning(c) && workQueue.offer(command)) {
	int recheck = ctl.get();
	if (! isRunning(recheck) && remove(command))
		reject(command);
	else if (workerCountOf(recheck) == 0)
		addWorker(null, false);
}else if (!addWorker(command, false))
	reject(command);

推入队列时是运行的,但是推入后线程池不是运行状态则拒绝线程提交。

拒绝指令提交的具体方法有很多,例如直接抛出异常,将工作队列队头任务取消,插入本次任务等等方式。

回顾问题—创建线程池时参数的作用?

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue) {
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
		 Executors.defaultThreadFactory(), defaultHandler);
}

那么回顾构造方法中的传入的参数集合:corePoolSize 核心线程数、maximumPoolSize 最大线程数、keepAliveTime 活跃时间、unit 活跃时间单位、workQueue 工作队列、defaultThreadFactory 默认的线程工厂、defaultHandler 默认的拒绝策略(不同的实现其拒绝策略是不一样的需注意)。

回顾下上述发掘的线程提交过程中,我们发现的三类情况:

  • 直接添加 worker
  • 推入工作队列
  • 拒绝处理指令

这三种情况的结果是由线程池当前运行的情况与配置的参数共同决定的。

核心解析线程池工作原理

ThreadPoolExecutor 的创建

通过前面的源码阅读分析,我们对线程池的创建有了大致的理解。无非就是根据给出的参数初始化一个线程池实例,它会拥有指定的核心Wroker数量,最大 Wroker 数量,工作队列的实例,线程创建的工厂类,以及线程池满的拒绝策略。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
		
        String name = Objects.toIdentityString(this);
        this.container = SharedThreadContainer.create(name);
    }

以上过程中,可能有疑惑的是代码段

        String name = Objects.toIdentityString(this);
        this.container = SharedThreadContainer.create(name);

其实它是创建线程容器的管理器,线程池的说法也是因为这个线程容器,并不是利用 Queue与 Queue并行执行指令的性质才命名为线程池。

这里着重探索下以下这段代码的作用与意义

	SharedThreadContainer.create(name);

    public static SharedThreadContainer create(String name) {
        return create(ThreadContainers.root(), name);
    }

    public static SharedThreadContainer create(ThreadContainer parent, String name) {
        if (parent.owner() != null)
            throw new IllegalArgumentException("parent has owner");
        var container = new SharedThreadContainer(name);
        // register the container to allow discovery by serviceability tools
        container.key = ThreadContainers.registerContainer(container);
        return container;
    }

    public static Object registerContainer(ThreadContainer container) {
        expungeStaleEntries();
        var ref = new WeakReference<>(container, QUEUE);
        CONTAINER_REGISTRY.add(ref);
        return ref;
    }

这里个人理解是创建并注册一个基于 root 线程容器 下的子线程容器,便于利用它控制我们提交的指令线程的生命周期(可能有问题,暂时先完成主线剧情)。

ThreadPoolExecutor 的第一次提交

那么我这里有以下测试代码片段

		CountDownLatch countDownLatch = new CountDownLatch(1);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 100, 0L,
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
        executor.submit(countDownLatch::countDown);
        countDownLatch.await();

不难看出提交了一个任务就是释放线程计数器的一个计数,那么首次提交,到底做了什么?

这里先抛出三个我思考了很久的问题:

  1. 到底是在哪里启动了 循环向 Queue中取指令消费的方法?
  2. 为什么设计了一个 Worker 的队列,又设计了一个 Queue队列?优点在哪?
  3. 核心线程数满载后为什么还可以提交指令到 Queue,这些阻塞的 Worker 在什么时候执行?如何执行?

可能以上三个问题的本身就存在问题,但是通过不断的阅读源码可以不断提升理解。带着问题找答案。

submit 方法在什么?

我们的始发点就是一个方法:

	public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

这里主要做了两个事情:

  • 创建一个的可执行的程序任务
  • 调用 execute 方法去执行这个可执行的程序任务

简单来看这个可执行程序任务的构造方法:

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

    private static final class RunnableAdapter<T> implements Callable<T> {
        private final Runnable task;
        private final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
        public String toString() {
            return super.toString() + "[Wrapped task = " + task + "]";
        }
    }

不难看出这个可执行程序任务是可以拿到返回结果的,Callable 可取结果。

但是重头戏是第一次提交中的 execute 方法。继续向下寻找源码。

execute 方法在做什么?

这段代码一开始阅读有点冗长,接下来我们慢慢看(其实最开始提交线程的分析时我们已经阅读过):

public void execute(Runnable command) {
		......省略部分......
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

分析的是首次提交,因此此时线程池中的工作队列都是清闲的,所以我们直接锁定:

		if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }

至于为什么 workerCountOf(c) < corePoolSize 能为真,我想上面咱们在 前序 中已经及其清晰的分析过,可以回顾一下,这里不再赘述。

自然的,核心就在于以下方法的代码段:

	private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (int c = ctl.get();;) {
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;

            for (;;) {
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();
                if (runStateAtLeast(c, SHUTDOWN))
                    continue retry;
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    int c = ctl.get();

                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        workerAdded = true;
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    container.start(t);
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

这里太长,我们根据首次提交的性质,直接找出重点执行的语句:

	        boolean workerStarted = false;
	        boolean workerAdded = false;
	        Worker w = null;
            w = new Worker(firstTask);
            final Thread t = w.thread;

			final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            int c = ctl.get();
            if (isRunning(c) ||
                (runStateLessThan(c, STOP) && firstTask == null)) {
                if (t.getState() != Thread.State.NEW)
                    throw new IllegalThreadStateException();
	                workers.add(w);
	                workerAdded = true;
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    }
                    mainLock.unlock();
                if (workerAdded) {
                    container.start(t);
                    workerStarted = true;
            }

我觉得还能再精简下:

Worker w = new Worker(firstTask);
final Thread t = w.thread;
workers.add(w);
container.start(t);

简单直接,首次提交没有那么多麻烦的东西,全都过,那么我们重点看看这四行代码能解决什么问题?

我们先看看 Worker 是啥构造?

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

那我们如果将那四行简单暴力的代码再精简一点

        Worker worker = new Worker(() -> run());
        Thread thread = worker.thread;
        thread.start();

这么一看,嗯,优雅。无言以对。

Worker 的 run( ) 方法

提交线程后,其实它们就执行或在前往执行的过程中了,我认为我们提交的不是线程,而是希望作为异步执行的程序脚本。异步的过程由线程池实现。

那么最后窥见一角的我们,来看看 run( ) 在干嘛?

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

就一行,跑 worker !一个队列而已,跑!

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock();
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    try {
                        task.run();
                        afterExecute(task, null);
                    } catch (Throwable ex) {
                        afterExecute(task, ex);
                        throw ex;
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

代码是很多,我们简化一波:

	Thread wt = Thread.currentThread();
	Runnable task = w.firstTask;
	w.firstTask = null;
	boolean completedAbruptly = true;
	while (task != null || (task = getTask()) != null) {
		beforeExecute(wt, task);
		task.run();
		afterExecute(task, null);
	}
	processWorkerExit(w, completedAbruptly);

我们发现循环内部执行的都很经典,执行线程前做啥?执行,执行后做啥?类似于 AOP 的通知,这个操作就是埋个钩子进去,在钩子里实现类中做想做的工作,便于直接获取每个 worker 的执行状态与执行管理,公司管这个叫报备与反馈。

实现循环的条件是 task != null || (task = getTask()) != null ,那么就是 getTask() 最为关键咯!

	private Runnable getTask() {
        boolean timedOut = false; 
        for (;;) {
            int c = ctl.get();
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);
			
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

基本操作,简化一波:

for (;;) {
	int c = ctl.get();
	int wc = workerCountOf(c);
	boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
	Runnable r =timed 
		? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 
		: workQueue.take();
	if (r != null)
        return r;
}

如果 worker 的数量已经大于核心线程数了,那么就等待 keepAliveTime 纳秒的时间获取任务,获取不到继续获取。直到线程池关闭或停止或 用户端不提交指令了。

运转流程图(不带状态控制)

画板