ThreadPoolExecutor 源码解析

194 阅读7分钟

一、ThreadPoolExecutor解析

1、线程池的5种状态

    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;

(1)RUNNING:接收新任务和队列任务

(2)SHUTDOWN:不接收新任务,但是接收队列任务

(3)STOP:不接收新任务,也不接收队列任务,并且打断正在进行中的任务

(4)TIDYING:所有任务终止,待处理任务数量为0,线程转换为TIDYING,将执行terminated钩子函数

(5)TERMINATED:terminated()钩子函数执行完成

2、状态之间的转换

(1)RUNNING -> SHUTDOWN:调用shutdown()方法

(2)(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()方法

(3)SHUTDOWN -> TIDYING:队列和线程池都是空的

(4)STOP -> TIDYING:线程池为空

(5)TIDYING -> TERMINATED:钩子函数terminated()执行完成

(使用时要调用shutdown()方法,然后调用awaitTermination方法)

3、默认的4种拒绝策略

(1)CallerRunsPolicy:调用execute()方法拒绝任务

(2)AbortPolicy:抛RejectedExecutionException异常(默认的处理方式

(3)DiscardPolicy:悄悄丢弃拒绝的任务

(4)DiscardOldestPolicy:丢弃最老的未处理的请求,然后重新执行execute()方法

二、源码分析

示例:

public static void main(String[] args) throws InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    executorService.execute(() -> System.out.println("hello"));
    executorService.shutdown();
    executorService.awaitTermination(1, TimeUnit.MINUTES);
}

Executors.newFixedThreadPool(1)只是做了一些日常的参数校验、判空操作、参数的赋值操作。

1、向线程池提交任务:execute()

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);
}

(1)当前线程池的线程数小于核心线程数时,直接添加核心线程数,把当前任务放进去

(2)如果核心线程数满了,把当前任务放到等待队列中去。放进去之后要重新再获取一次当前线程池的状态,如果获取的结果是没有运行,则把当前任务给移除,同时执行拒绝策略(为啥做这一步,因为是多线程操作,有可能在往队列里放任务时,外部已经把线程池给关闭了,所有才需要重新获取一次)。

如果获取到的线程池是正在运行且工作线程数为0,则添加非核心线程(为啥是添加非核心线程,因为允许核心线程睡眠,释放资源,默认是不允许核心线程睡眠的,临界情况,实际使用时可以忽略)

(3)如果队列也满了,则添加非核心线程数,添加失败时执行拒绝策略

2、创建新线程:addWorker(Runnable firstTask, boolean core)

(1)此段代码检测队列是否为空。不为空才能添加线程。

if (rs >= SHUTDOWN &&
    ! (rs == SHUTDOWN &&
       firstTask == null &&
       ! workQueue.isEmpty()))
    return false;

拆分即为以下步骤

1)如果当前运行状态 >= SHUTDOWN,继续;否则(即为Running)不进行后续的判断,执行后续的代码

2)如果当前运行状态 = SHUTDOWN(不接收外部任务,但是队列任务还要继续执行),继续;否则(!= SHUTDOWN,此时即为STOP或TIDYING或TERMINATED状态),返回false,表示已经关闭线程池了,自然就不能addWorker了。

3)如果是新提交的任务(firstTask == null),继续;否则(不是新任务,老任务就不用再addWorker了),返回false。

4)如果队列是空,返回false。

(2)此段代码就是让线程数加1

retry:
for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c);
    ......
    for (;;) {
        int wc = workerCountOf(c);
        if (wc >= CAPACITY ||
            wc >= (core ? corePoolSize : maximumPoolSize))
            return false;
        if (compareAndIncrementWorkerCount(c))
            break retry;
        c = ctl.get();  // Re-read ctl
        if (runStateOf(c) != rs)
            continue retry;
        // else CAS failed due to workerCount change; retry inner loop
    }
}

细分即为以下步骤

1)取工作线程的数量。如果工作线程数大于等于队列的最大容量(2^29-1)或大于核心线程数(添加核心线程时)或大于最大线程数(添加非核心线程时),返回false,不能再添加线程了;否则,继续

2)使用CAS的方式把任务线程的数量加1,添加成功则跳出外层for循环(break retry);否则,继续

3)添加线程失败(为啥会添加线程数量失败,肯定是有其他线程修改了线程池的状态),重新读取线程的状态,重新判断线程的运行状态(判断线程是否调用了shutdown方法,修改了线程池的状态),如果不等于之前的线程池状态,退出循环(continue retry),不能增加线程

(3)创建线程、启动线程

经过前两步,(第一步)可以添加线程、(第二步)且线程数量已经加1,接下来就是创建线程并启动线程

boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
    w = new Worker(firstTask);
    final Thread t = w.thread;
    // 打工人没有锤子肯定干不了活啊,所以 t != null
    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 rs = runStateOf(ctl.get());

            if (rs < SHUTDOWN ||
                (rs == SHUTDOWN && firstTask == null)) {
                if (t.isAlive()) // precheck that t is startable
                    throw new IllegalThreadStateException();
                // 往线程池添加刚刚新建的线程
                workers.add(w);
                int s = workers.size();
                if (s > largestPoolSize)
                    largestPoolSize = s;
                workerAdded = true;
            }
        } finally {
            mainLock.unlock();
        }
        // 添加线程成功,则启动线程
        if (workerAdded) {
            t.start();
            workerStarted = true;
        }
    }
} finally {
    // 启动线程失败,则移除线程,同时线程数减1
    if (! workerStarted)
        addWorkerFailed(w);
}
return workerStarted;

细分即为以下步骤

1)new Worker(firstTask),里面有3个变量:线程、第一个任务、任务完成数量(这三个变量很好理解,打工人打工魂,打工人没有锤子<线程>怎么打工,同时办完入职手续后得知道要干的第一个活啊,同时完成的活还得记录已经完成的数量),new的时候给了工具和第一个活。

2)上锁(因为是多线程操作),添加new出来的Worker,然后启动线程。

3)如果启动线程失败,则移除Worker,同时工作线程数量减1。

3、线程的主循环Worker:runWorker(Worker w)

一直执行任务,在执行任务前和后都可以进行扩展。想要获得线程池执行时的异常,可以重写钩子函数afterExecute 或手动捕获task.run()的异常 或使用Future去get

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 状态值加1
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 如果第一个任务不为空,则执行第一个任务
        // 第一个任务为空,则从队列获取任务执行
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 如果当前线程STOP了,则直接设置中断标志位
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 // interrupted会清除中断标记
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                // isInterrupted(不会清除中断标记),为false表示未中断,重新设置中断标记
                // isInterrupted为true,已经中断
                !wt.isInterrupted())
                wt.interrupt();
            try {
                // 钩子函数
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 执行任务
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    // 钩子函数
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 全部任务执行完毕,执行退出操作
        processWorkerExit(w, completedAbruptly);
    }
}

细分即为以下步骤

1)w.unlock(); // allow interrupts?new Worker的时候,线程还没有启动,没有启动怎么能中断呢,所以给了一个-1表示不能中断,而此时runWorker进来时,线程已经启动,就允许中断,而此时unlock是加1,则此时状态值为0,表示可以中断。

2)如果第一个任务不为空,继续;否则,从队列取任务,取出任务不为空,继续;取出任务为空,则表示所有的任务都执行完毕,执行退出操作

3)如果当前线程STOP了,则直接设置中断标志位;否则,手动判断线程是否中断(防止在业务代码中手动设置了中断标志位)是,则再判断当前线程是否中断了,false则整个判断就结束了,然后设置中断标志位;true(表示线程已经停止了)继续

4)Thread.interrupted(),会把中断标志位干掉,wt.isInterrupted()为false会再设置中断标志位,因为线程已经停止了<runStateAtLeast(ctl.get(), STOP))>所以需要设置中断标志位。(如果任务已经中断过一次,结果又去拿了另外一个任务过来,但是线程池已经STOP了,就需要判断是否中断了,没有就中断,已经中断了就不用再中断了)

5)执行前和执行后都有一个钩子函数,beforeExecute和afterExecute,留给子类扩展实现的

6)执行任务

7)firstTask置空,当前完成的任务数加1

有一个问题:在执行afterExecute时,里面的异常会被finally给吞掉,也就是在执行afterExecute出现异常时,后续的代码还会继续执行,需要自己手动捕获处理。 为啥在执行task = getTask()时需要捕获异常:getTask()没有任务时会阻塞等待,但是线程结束时得唤醒它的阻塞等待,需要判断是否由中断导致的线程退出

造成completedAbruptly为true有三个地方:getTask()、beforeExecute和afterExecute出现异常。

造成completedAbruptly为false,只有getTask()为空时。

4、从队列中获取排队的任务:getTask()

5、线程结束:

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

        tryTerminate();

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

1)如果completedAbruptly为true(由于用户异常<即beforeExecute和afterExecute中的异常>造成的工作线程死亡),工作线程数减1,否则不减,继续

2)把当前线程移除,继续

3)如果处于Running或Shutdown状态(runStateLessThan(c, STOP)),继续;否则方法执行完毕

4)如果是用户异常造成的,addWorker(null, false),新启了一个非核心线程;

if (!completedAbruptly) {
    int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
    if (min == 0 && ! workQueue.isEmpty())
        min = 1;
    if (workerCountOf(c) >= min)
        return; // replacement not needed
}
addWorker(null, false);

如果不是用户异常造成的(completedAbruptly为false),如果允许核心线程超时且队列不为空,而此时工作线程数大于等于1,方法直接结束,如果此时工作线程数小于1,则addWorker(null, false),启动一个非核心线程,也就是保证至少有一个线程在执行任务