ThreadPoolExecutor源码解析

变量含义
  • ctl既表示workerCount(有效线程数),也表示runState(线程池状态)。其值为负数时表示线程池状态,为正数时表示线程数。

    • ctl 的初始值:-536870912
    • |    高位     |    低位    |
    • | -536870912 —> 0 —> 536870912 |
    • |   线程状态    |   线程数量   |
  • runState的几个常量实际上是范围,是边界值,并非固定的值,只不过这几个状态的范围有数值顺序,可以用大于、小于比较。

    • RUNNING正常接收任务
    • SHUTDOWN不接收新任务,继续处理已存在的任务
    • STOP不接收新任务,也不会处理队列中的任务,同时中断正在进行的任务
    • TIDYING过渡状态,表示当前线程池即将终止。已经执行完shutdownshutdownNow方法,准备终止线程池
    • TERMINATED表示线程池已终止,要执行terminated方法才到这个状态
  • runStateOf方法:线程池状态,返回负数说明是RUNNING,0为SHUTDOWN,理论上不会返回正数。

  • workerCountOf方法:线程数量,随着ctl++,返回值也是0开始递增,直到 536870911 & 536870911 = 536870911workerCount不包含队列中的任务。

  • Worker对象 继承自AQS,可以方便的实现工作线程的终止操作。

  • COUNT_BITS int变量:长度。29,方便做位运算。

  • CAPACITY int变量:容量。线程池中线程池的最大数量。

  • SHUTDOWN --> TIDYING 队列为空,工作线程为空时。

  • STOP --> TIDYING 工作线程为空时。

  • TIDYING --> TERMINATED 内部调用方法,状态变为TERMINATED。

源码解析
execute

execute解析,线程池执行流程

// 这个方法没有加锁,所以有多次recheck的操作
public void execute(Runnable command) {
    if (command == null)
    	throw new NullPointerException();
	// 分3步进行:
	// 1. 如果正在运行的线程少于corePoolSize,尝试新开启一个线程
	//	将command作为它的第一个任务。对 addWorker 的调用
	//	以原子方式检查 runState 和 workerCount。
	//
	// 2. 如果一个任务成功进入队列,仍然需要检查是否应该添加一个线程
	//	可能期间其他线程调用线程池的shutdown 或 terminate相关方法。
	//	因此,要重新检查状态,
	//	if 不是running,移除刚入队的任务。
    //	else-if 线程数为0(因为是else-if,此时必定是running),则启动一个新线程。
	//
	// 3. 经过以上2步,既无法加入队列,也无法添加新线程,则要么shutdown 要么已饱和
	//	此时拒绝该任务
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        // 这里 可能因为并发等原因,导致创建线程并add失败,重新获取ctl
        c = ctl.get();
    }
// Q-1
	// 如果是STOP或SHUTDOWN,不接收新任务。如果是RUNNING,到这里表示创建核心线程池失败,要加入队列
    if (isRunning(c) && workQueue.offer(command)) {
        // 怕出现并发问题
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 结论:这个if表示. 有任务,但没有工作线程来处理,需要新建一个任务为空的工作线程来处理队列中的任务
        // 这一步可能出现的原因:如果线程池只有一个线程T1在运行,main走到这里,T1运行完成,
        //	ctl执行 -1 操作,此时线程数为0,结果为true 进入if。
        // 为什么第一个参数为null?
        //	在addWorker时,任务已经入队,在runWorker方法中,
        //	如果worker的firstTask为null,那么会从workQueue队列里取任务执行,
        //	此处传null让 addWorker 方法内部有机会执行 t.start(),从而之行runWorker方法
        // 这里可以得出,队列中的任务不包含在workerCount中
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 到这里有以下2种情况:
    // 1. running状态,入队失败(队列已满)。创建非核心线程来处理任务
    // 2. 不是running状态,即c >= SHUTDOWN
    // 直接调用 addWorker 方法,是因为里面有判断ctl状态的方法,为SHUTDOWN返回fasle
    else if (!addWorker(command, false))
        reject(command);
}
问题
  • 为什么代码Q-1处要使用workQueue.offer而不是put等阻塞方法?
    答:一般是主线程调用execute方法,如果阻塞,影响后续代码执行。
addWorker

进入这个方法有2种情况:

  1. 核心线程数小于正在执行任务的线程数。
  2. 核心线程数已满 && 当前线程数<=最大线程数 && 任务入队成功。
private boolean addWorker(Runnable firstTask, boolean core) {
    // for:CAS更新线程池数量
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 1. 不是RUNNING状态
        // 2. 且,线程池状态不等于SHUTDOWN or firstTask为null or 队列为空
        // 必须是SHUTDOWN、firstTask==null、workQueue==空,才走else,对应execute的最后一次调用
        // 满足上述条件直接返回false
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            // 线程数量大于CAPACITY or (线程数量大于核心线程数or最大线程数),返回false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
        	// ctl自增workCount,成功则结束整个循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // workCount更新失败,重新获取ctl的值(出现并发了)
            c = ctl.get();  // Re-read ctl
            // 状态已经改变,则继续外层循环。否则continue内层循环重试
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

// 上面主要是对 worker 数量做原子+1 操作,下面的逻辑才是正式构建一个 worker

    // 工作线程是否启动标识
    boolean workerStarted = false;
    // 工作线程是否添加成功标识
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 构建一个worker,里面是任务
        w = new Worker(firstTask);
        // 从worker里取出线程对象
        final Thread t = w.thread;
        if (t != null) {
            // 线程池全局锁,防止当前线程添加任务时,其他线程kill了线程池
            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());

                // 线程池是RUNNING[或 是SHUTDOWN但传过来的空任务 与上面对应,创建空线程来处理队列的任务],才能添加到队列中
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    // 任务刚来,还未添加启动,就已经alive状态,这里不对,所以要抛异常
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 加入worker集合。workers是一个HashSet
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        // 更新线程池的最大线程数
                        largestPoolSize = s;
                    // 任务添加成功标识 
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 任务添加成功 启动线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // 任务启动失败,递减工作线程数量
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}
问题

addWorker做了2件事,1-递增ctl;2-task加入 workers集合并start(线程池中的线程)

  • 传入的Runnable类型的任务紧接着就进行start了,那么为什么还要workers.add(w)放入集合呢,workers集合存在的意义是什么呢?(addWorker方法在什么线程里执行的?)
    答:为了保存当时的thread和worker,后续可能会对thread和worker进行加锁和中断,addWorker和runWorker是并行关系,需要随时监视shutdown,shutdownNow,terminate的动作。
  • 线程池(ThreadPoolExecutor)中存放的是线程吗?
    答:不是,是一堆Worker,Worker既不是thread线程也不是要执行的任务。
new Worker(Runnable firstTask)
  • Worker实现AQS,内部实现是一个独占锁,不允许重入,为了针对不同的worker进行中断。
  • 为什么不用Lock而是需要实现AQS,主要是不能允许重入的,在中断线程时,竞争锁资源。
Worker(Runnable firstTask) {
    // 刚初始化,不允许被中断
    setState(-1);
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}
runWorker [woker.run()]

addWorker的t.start()触发了该方法。执行任务的流程,并且做了能否中断的判断(Worker的state状态)

final void runWorker(Worker w) {
    // 获取当前线程并拿到任务
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
	// 任务归位
    w.firstTask = null;
	// worker的lock用于区分线程是否空闲的(AQS的release)
    w.unlock(); // allow interrupts
	// 是否发生了意外
    boolean completedAbruptly = true;
    try {
        // 任务不空。直接执行
        // 任务空,通过getTask()获取任务
        while (task != null || (task = getTask()) != null) {
            // worker加锁,这样即便shutdown 任务也不会中断
            w.lock();
            // 如果线程池正在停止,确保线程被中断;如果不是,确保线程不会被中断,
            // 第二种情况时,需要重新检查以清除中断时shutdownNow的竞争。
            // 上面说SHUTDOWN不会中断,但STOP/TIDYING/TERMINATED时:中断任务
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                // 子类自定义实现
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // do run
                    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
                task = null;
                // 完成的任务数+1
                w.completedTasks++;
                // worker的state设置为0,表示调用shutdown可以中断
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}
getTask()

获取队列中的任务

private Runnable getTask() {
    // 非核心线程可以干掉
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        // ================判断线程池状态==============
        int c = ctl.get();
        int rs = runStateOf(c);

        // 线程池状态不是RUNNING,且,STOP/TIDYING/TERMINATED or 队列为null。移除当前工作线程
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            // 递减工作线程数量。这里并没有从workers里remove,remove是在runWorker方法的finally代码块里
            decrementWorkerCount();
            return null;
        }

        // ================判断工作线程数量 ==============
        int wc = workerCountOf(c);

        // 是否允许核心线程池超时 or 工作线程数量 > 核心线程数
        // 第二种情况,判断是否需要干掉自己
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if (
            // 工作线程数大于最大线程数(基本不可能)
            // or 工作线程数大于核心线程数,且 该工作线程已经超时
            (wc > maximumPoolSize || (timed && timedOut))
            // 且 干掉自己后 至少还有一个工作线程
            // or 队列为空
            // ========以上满足则会干掉当前线程
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        // ================从工作队列中获取任务 ==============
        try {
            // 核心线程允许超时 or 此时是非核心线程在干活。走poll,超时就返回
            // 核心线程不允许超时 and 核心线程在干活。走take方法,一直阻塞
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            // 没拿到任务,表示走的是poll,从队列获取任务超时,即达到了工作线程的keepAliveTime
            timedOut = true;
        } catch (InterruptedException retry) {
            // take时被中断,再来一遍
            timedOut = false;
        }
    }
}
processWorkerExit

移除当前工作线程

  1. 当前工作线程需要被清理
  2. 当前线程池状态不对,需要移除
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // before或after钩子函数抛出了异常(不是getTask方法调用)
    if (completedAbruptly)
        // 递减工作线程数
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 记录当前线程池一共处理过多少任务。前面加锁跟这个有关
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
	// 尝试将线程池关闭,(--> TIDYING --> TERMINATED)
    tryTerminate();

    int c = ctl.get();
    // 线程池状态RUNNING or SHUTDOWN。否则里面的代码无需执行
    if (runStateLessThan(c, STOP)) {
        // 是否有异常。completedAbruptly = false:没有异常,true:有异常
        // if 正常状态移除当前工作线程
        if (!completedAbruptly) {
            // 允许最小的核心线程数
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 最小的核心线程数是0,and 队列不为空。设置工作线程最小值为1,表示至少要有1个线程去处理任务 
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 还有工作线程在线程池中,确保任务有线程执行。直接return,否则再addWorker一个null任务的线程
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 不是正常状态下移除当前工作线程,
        // or 队列有任务但没有工作线程了。需要新增一个工作线程处理队列中的任务
        addWorker(null, false);
    }
}
参考资料

ThreadPoolExecutor源码细节探索 - 掘金

  • 40
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值