记一次:线程池源码解析

前言:很多时候我们需要使用线程池来处理逻辑,但实际上线程池是如何添加线程,如何执行的呢?

0:创建线程池--略(7个参数)

1:提交线程池源码

public void execute(Runnable command) {
        //判空没啥可说
        if (command == null)  throw new NullPointerException();
        //获取核心属性
int c = ctl.get();
        //workerCountOf(c)获取工作线程个数
    //当前工作线程个数< 核心线程数
        if (workerCountOf(c) < corePoolSize) {
        //添加一个工作线程  true:核心线程 false:非核心线程
        //添加工作线程有并发问题,若成功返回true,失败返回false
            if (addWorker(command, true))
            //成功,告辞~
                return;
        //添加失败,重新获取ctl,看一下当前线程情况
            c = ctl.get();
        }
   // isRunning:线程池状态还是RUNNING嘛?
   // 如果是RUNNING,就把任务扔到阻塞队列里,先排队等着
        if (isRunning(c) && workQueue.offer(command)) {
        //再次获取ct1属性
            int recheck = ctl.get();
        //再次确认线程池状态是否是RUNNING,如果不是RUNNING,将任务从阻塞队列移除
            if (! isRunning(recheck) && remove(command))
            //执行拒绝策略
                reject(command);
        //现在阻塞队列有任务,但是没有工作线程
            else if (workerCountOf(recheck) == 0)
            //创建一个非核心线程,去处理阻塞队列任务
                addWorker(null, false);
        }
    // 如果任务没有扔到阻塞队列,添加非核心线程去处理任务
        else if (!addWorker(command, false))
            reject(command);
  }

太简洁了,和我编程风格不习惯,本人优化一下,一毛一样的

//提交任务到线程池的方法
public void execute(Runnable command) {
    //判空没啥可说
    if (command == null)  throw new NullPointerException();
    //获取核心属性
    int c = ctl.get();
    //workerCountOf(c)获取工作线程个数
    //当前工作线程个数< 核心线程数
    if (workerCountOf(c) < corePoolSize) {
        //添加一个工作线程  true:核心线程 false:非核心线程
        //添加工作线程有并发问题,若成功返回true,失败返回false
        if (addWorker(command, true)){
            //成功,告辞~
            return;
        }
        //添加失败,重新获取ctl,看一下当前线程情况
        c = ctl.get();
    }
    // isRunning:线程池状态还是RUNNING嘛?
    // 如果是RUNNING,就把任务扔到阻塞队列里,先排队等着
    if (isRunning(c) && workQueue.offer(command)) {
        //再次获取ct1属性
        int recheck = ctl.get();
        //再次确认线程池状态是否是RUNNING,如果不是RUNNING,将任务从阻塞队列移除
        if (! isRunning(recheck) && remove(command)){
            //执行拒绝策略
            reject(command);
            //现在阻塞队列有任务,但是没有工作线程
        }else if (workerCountOf(recheck) == 0){
            //创建一个非核心线程,去处理阻塞队列任务
            addWorker(null, false);
        }
     //如果任务没有扔到阻塞队列,添加非核心线程去处理任务
    }else if (!addWorker(command, false)){
        reject(command);
    }
}

 好了,这回看着舒服多了,下面开始画流程图吧

2、流程图详解

 3、添加工作线程的流程(addWorker方法)

源码如下:

private boolean addWorker(Runnable firstTask, boolean core) {
    //goto 语句,避免死循环
    retry:
    // 外层自旋
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 这个条件写得比较难懂,我对其进行了调整,和下面的条件等价
        // (rs > SHUTDOWN) ||
        // (rs == SHUTDOWN && firstTask != null) ||
        // (rs == SHUTDOWN && workQueue.isEmpty())
        // 1. 线程池状态大于SHUTDOWN时,直接返回false
        // 2. 线程池状态等于SHUTDOWN,且firstTask不为null,直接返回false
        // 3. 线程池状态等于SHUTDOWN,且队列为空,直接返回false

        //通俗的解释
        //(1). 线程池已经 shutdown 后,还要添加新的任务,拒绝
        //(2).(第二个判断)SHUTDOWN 状态不接受新任务,但仍然会执行已经加入任务队列的任
        //务,所以当进入SHUTDOWN 状态,而传进来的任务为空,并且任务队列不为空的时候,
        //是允许添加新线程的,如果把这个条件取反,就表示不允许添加 worker
        if (rs >= SHUTDOWN &&  ! (rs == SHUTDOWN && firstTask == null &&  ! workQueue.isEmpty())){
            return false;
        }
        // 内层自旋
        for (;;) {
            //获得 Worker 的工作线程数
            int wc = workerCountOf(c);
            // worker数量超过容量,直接返回false
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)){
                return false;
            }
            // 使用CAS的方式增加worker数量。如果 cas 失败,则直接重试
            // 若增加成功,则直接跳出外层循环进入到第二部分
            if (compareAndIncrementWorkerCount(c)){
                break retry;
            }
            //再次获取 ctl 的值
            c = ctl.get();
            // //这里如果不想等,说明线程的状态发生了变化,继续重试
            if (runStateOf(c) != rs){
                continue retry;
            }
            // 其他情况,直接内层循环进行自旋即可
        }
    }
    //上面这段代码主要是对 worker 数量做原子+1 操作,下面的逻辑才是正式构建一个 worker

    //工作线程是否启动的标识
    boolean workerStarted = false;
    //工作线程是否已经添加成功的标识
    boolean workerAdded = false;
    Worker w = null;
    try {
        //构建一个 Worker,这个 worker 是什么呢?我们可以看到构造方法里面传入了一个 Runnable 对象
        w = new Worker(firstTask);
        //从 worker 对象中取出线程
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            // worker的添加必须是串行的,因此需要加锁
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                // 这儿需要重新检查线程池状态
                int rs = runStateOf(ctl.get());
                //只有当前线程池是正在运行状态,[或是 SHUTDOWN 且 firstTask 为空],才能添加到 workers集合中
                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                    // worker已经调用过了start()方法,则不再创建worker
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // worker创建并添加到workers成功
                    workers.add(w);
                    // 更新`largestPoolSize`变量
                    int s = workers.size();
                    //如果集合中的工作线程数大于最大线程数,这个最大线程数表示线程池曾经出现过的最大线程数
                    if (s > largestPoolSize){
                        //更新线程池出现过的最大线程数
                        largestPoolSize = s;
                    }
                    //表示工作线程创建成功了
                    workerAdded = true;
                }
            } finally {
                //释放锁
                mainLock.unlock();
            }
            //如果 worker 添加成功
            if (workerAdded) {
                //启动worker线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        //如果添加失败,就需要做一件事,就是递减实际工作线程数(还记得我们最开始的时候增加了工作线程数吗)
        if (! workerStarted){
            addWorkerFailed(w);
        }
    }
    return workerStarted;
}

流程图也贴出来,自己根据源码画的,有不对的地方多包涵

 Worker封装和addWorkerFailed的解析--后续补上

猜你喜欢

转载自blog.csdn.net/sinat_38259539/article/details/132319188