线程池TheadPoolExecutor执行过程

138 阅读13分钟

一、execute执行代码解析

  • 当我们把要执行线程的时候 都会调用线程池的execute方法进行执行 execute里的代码如下
  • 在之前有个知识点需要说下 线程池有个核心变量为 并发32位整数AtomicInteger ctl 这个数核心是为了记录当前线程池的状态和当前的工作线程数 之所以特意提32位 是因为线程池把前3位用来记录状态。后29位用来记录工作线程数 当ctl整数转换成二进制 11100000000000000000000000000000 前三个111代表线程池在运行中 11100000000000000000000000000001 这个代表线程池在运行 并且有一个工作线程 提供了两个方法runStateOf(获取当前状态) workerCountOf(获取当前数量)
  1. RUNNING 运行 线程池启动的初始化状态 前3位为111
  2. SHUTDOWN 关闭状态,不再接受新提交的任务 不过已经在阻塞队列里的任务会继续进行 我们执行线程池的shutown方法就是把状态变成关闭状态 前3位为000
  3. STOP 停止 不再接受新提交的任务 并且不会处理阻塞队列里的任务 并且会中断当前执行的线程 不过这里中断主要是执行interrupt方法 所以业务线程不会立刻中断 有可能还在运行 原因可以搜索下interrupt 当调用线程池的shutdownNow就会设置这个状态 前3位为001
  4. TIDYING 清理完成 当所有工作线程都没有 workerCountOf为0 就会进入这个状态 前3位为010
  5. TERMINATED 终止状态 当前清理完成以后 线程池会执行terminated这个方法 这个主要是为了给需要在线程终止的时候处理业务业务逻辑而提供的回调 线程池本身是没有实现的 这个方法执行完以后状态就会变成终止状态 ‭前3位为011
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        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);

先判断当前线程池的工作线程是否小于核心线程数 是则调用addWorker添加工作线程 如果添加失败则往下走 成功则直接返回

        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
  1. 然后判断当前线程池状态isRunning 防止线程被关闭 之所以在上面没有判断是因为addWorker方法里面也有判断线程池状态
  2. 如果线程池在运行则调用队列的offer方法插入队列 如果插入成功 则会进行二次状态check
  3. 如果状态不为运行状态则会进行删除前面放入队列线程任务remove
  4. 之所以二次状态check是为了防止插入队列以前线程池正常 插入队列以后被关闭了 如果关闭则应该要把前面放入队列的任务移除 然后执行拒绝策略 如果remove失败代表已经有工作线程在执行了刚刚放到队列里的任务了 那也就不需要执行拒绝策略了
  5. 如果状态还是运行状态 则判断当前是否没有工作线程workerCountOf 之所以在这里判断 是为了防止核心线程数设置为0的情况 当为0 则第一次执行的时候 代码走到这里的时候还没有建立工作线程 就需要在这里新增一个工作线程来消费队列里的任务 addWorker(null, false)这里传的null 是因为要执行的线程已经放到队列里了workQueue.offer(command)
        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);
        }

如果超过核心线程 并且队列也放不下了 就会直接新增工作线程addWorker 如果新增失败 则会走拒绝策略

        else if (!addWorker(command, false))
            reject(command);

二、addWorker执行代码解析

接下来说下添加工作线程的代码 在说这部分前 先说下什么是工作线程 初次用线程池的人可能以为自己把Runnable扔给线程池以后 线程池会创建一个Thread来start扔的Runnable 而实际线程池定义里一个工作线程Worker 这个对象也继承了Runnable 实际异步执行的是这个Worker 我们设置的最大核心线程数 最大线程数其实就是Worker的数量

       retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

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

        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 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 {
                   Java mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;

一开始 先对线程池的状态和工作线程队列进行效验 下面的判断可以为下面的比较好看懂

rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())

  • 如果当前线程池状态>= SHUTDOWN(SHUTDOWN || STOP || TIDYING || TERMINATED) 代表线程池要关闭 反之代表线程池正常运行则继续执行
  • 然后线程池为SHUTDOWN状态 而工作线程为空的情况下workQueue.isEmpty 代表工作线程都已经结束了 则不会继续执行
  • 如果为SHUTDOWN状态 而工作线程还有 不过firstTask不为空 则也不会继续执行 firstTask不为空的情况主要是要新增核心线程数活着缓存队列放不下的情况下 则不会继续执行
  • 如果为SHUTDOWN状态 而工作线程还有 firstTask为空的情况 这里目前想到的对应场景是上面说的核心线程池为0的场景 任务已经放到队列里了 而SHUTDOWN状态是等队列里的任务执行完才会关闭 所以需要建立工作现场先处理完队列里的线程任务 则继续执行
  • 其他情况 都为继续
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

上面的判断过了以后 会判断工作线程数的限制

  1. 如果是新增核心工作线程core 当前工作线程数是否>=核心线程数corePoolSize 是则不执行
  2. 如果不是新增核心工作线程 当前工作线程数是否>=最大线程数maximumPoolSize 是则不执行
  3. 如果都可以 则cas更新工作线程数量compareAndIncrementWorkerCount 更新成功则跳出循环 注意 这里和上面是两层for循环 所以除了break 还加了retry来跳出两层循环 不然只有break 则只跳了下面这层循环
  4. 如果没有更新成功 并且线程池状态有变化runStateOf(c) != rs 则跳出下面的循环 continue retry 继续上面的循环判断状态 这里改成break也是可以一样的效果
  5. 如果线程池状态没有变化 则继续循环cas更新
            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
            }

创建一个工作线程 并且把业务扔过来的Runnable放入到工作线程里 有个核心点是this.thread = getThreadFactory().newThread(this) 创建一个线程 并且把工作线程放入到Thread中 这样启动这个thread执行的其实是工作线程

            w = new Worker(firstTask);
       
       Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

接下来进行加锁 主要有两个原因

  1. 为了防止并发把工作线程放Worker到工作线程队列HashSet Worker workers里
  2. 当线程池关闭的时候 也会先获取锁 再进行处理
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();

获取线程池状态

  1. rs < SHUTDOWN 代表是正常状态RUNNING 则把工作线程放到工作线程队列里workers.add(w)
  2. 如果是SHUTDOWN状态 并且firstTask=null 则把工作线程放到工作线程队列里 原因则是为了上面说的核心线程数为0的场景
  3. 其他状态 则不执行
                    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;
                    }

解锁 如果添加工作线程成功workerAdded 则启动线程t.start()

                } finally {
                   Java mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;

三、Wroker执行过程

上面讲了增加线程池工作线程数compareAndIncrementWorkerCount 创建工作线程worker 并且放入工作线程队列中workers 然后启动线程t.start 这时候线程池的的execute方法已经执行完毕 然后实际执行业务传下来的Runnable 则是在另外的线程里异步执行 接下来看下Wroker的执行过程

run方法执行的是runWorker方法

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

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !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);
        }
    }

runWorker的核心就是这段代码 firstTask就是第一次新增工作线程时候业务传的业务线程Runnable 在工作线程小于核心线程数或者缓存队列满了 新增工作线程的时候 firstTask不为空 如果不为空 则继续执行 如果为空 则从缓存队列里获取待执行的业务线程Runnable task = getTask()

Thread wt = Thread.currentThread()
Runnable task = w.firstTask;
while (task != null || (task = getTask()) != null)

当要执行业务线程时会先判断线程池的状态 如果是STOP状态 并且当前线程wt不是中断状态 则会设置中断状态 之所以中断了还继续执行 是因为这个中断只是设置了中断的标志位 线程还是会执行 然后执行业务线程的时候 如果业务线程有判断这个中断标识位或者在sleep的时候 则会触发销毁线程 所以当业务线程没有判断中断标识或者在sleep、join之类阻塞等待的情况下 线程池关闭如果业务线程以及被取出来执行了 则会等待业务线程执行完才会关闭 如果业务线程写了个死循环 线程池自身则一直都无法关闭 如果业务线程执行完了 则不会再从队列里获取 然后关闭线程 这里加个补充

原先看到这里的时候想着一般业务代码不会判断中断标识或者使用sleep来等待 所以感觉这个没什么用 所以想着如果一个线程里比如调用mysql卡住了 导致线程会一直执行着 也就是不会关闭 所以容易出现线程池一直关闭不掉 后来想了下 我们业务代码确实不会判断中断标识什么的 可一般我们业务代码除了写了死循环以外 需要执行很久的代码其实很少 大部分都是因为调用外部接口导致阻塞 比如redis、mysql、dubbo、mq 一般业务代码也就调用这些外部服务 而这些客户端的io操作其实大部分默认都是阻塞等待结果返回的 一般都是用的LockSupport.park 其他的阻塞方式 只要设置了线程中断标识都会唤醒或者直接抛异常 也就是说一般的业务线程确实会因为线程池关闭以后 就会很快关闭线程

        if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()){
                    wt.interrupt();
    }
  1. 接下来开始正式执行业务线程 会先执行前置方法beforeExecute 这个是一个空实现 是一个扩展点 可以在需要的情况下实现
  2. 核心是task.run() 执行业务线程 这个就是我们放到线程池里要执行的业务线程
  3. 当执行完业务线程以后 会执行后置方法afterExecute 这里也是一个扩展点
  4. 有个注意的点是看到如果run方法执行异常 捕捉以后 还是会继续抛异常出来throw x 这里会涉及到一个知识点 可以看我另一篇文章juejin.cn/post/684490…
                   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);
                    }

之后设置要执行的业务线程为空 并且累加当前工作线程执行的业务线程数量 解锁 然后继续执行循环 从缓存队列里获取要执行的任务

                    task = null;
                    w.completedTasks++;
                    w.unlock();

如果退出循环 则会执行下面的代码 设置为不是因为异常退出循环completedAbruptly=false 如果是因为执行run方法异常了 不会执行设置false这行代码 也就是completedAbruptly会等于true 然后执行processWorkerExit

            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }

下面的是processWorkerExit代码

        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 decrementWorkerCount

        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

  1. 把当前累加的工作线程执行的业务线程数量加到整个线程池执行业务线程数
  2. 把工作线程从工作线程队列里移除
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }

下面的方法如果线程池是正常运行的状态不会执行里面的业务 只有在要关闭的情况才会执行

        tryTerminate();

下面的代码主要是为了针对执行run方法的时候业务线程抛异常 导致工作线程意外结束的情况 会新增一个工作线程


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

四、getTask方法

上面讲的是worker线程从执行到销毁的步骤 可以看到里面的核心是里面的while循环while (task != null || (task = getTask()) != null) 通过这个循环来做到线程池通过不超过最大工作线程数 来处理大量的线程接下来讲下获取业务线程的方法getTask

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            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;
            }
        }
    }

设置timedOut为false

  • rs >= STOP 如果这个条件成立 则代表是执行了线程池的shutdownNow方法设置了状态为停止状态 则不会继续执行
  • rs >= SHUTDOWN && workQueue.isEmpty() 则代表如果是关闭状态 则是执行了shutdown 只有业务线程缓存队列都没有的情况下才会不继续执行 也是因为这个判断 所以前面才会说执行shutdown方法 会等待缓存队列里的任务执行完了 才会线程池关闭
        boolean timedOut = false; // Did the last poll() time out?

            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

下面核心是timed这个参数 当当前工作线程大于核心线程 或者allowCoreThreadTimeOut这个参数为true的情况下 timed为true timed这个参数的作用我下面再讲 allowCoreThreadTimeOut这个参数是可以调用线程池的allowCoreThreadTimeOut()方法进行设置的

            // Are workers subject to culling?
            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;
            }

先说第二段代码

  1. 当timed为true 代表当前工作线程数大于核心线程数 或者allowCoreThreadTimeOut为true
  2. 则会执行poll方法 设置超时时间 如果达到超时时间还是没有从缓存队列里拿到要执行的业务线程任务 则r为空 超时标志为true timedOut 也就代表当allowCoreThreadTimeOut为true时 核心线程也会超时回收 所以核心线程不是一定会一直有的
  3. 当timed为false 代表当前工作线程数小于核心线程数 或者allowCoreThreadTimeOut为false
  4. 则会执行take方法 一直阻塞等待 一般普通核心线程数就是一直等待着
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }

这段代码核心其实是为了上面当执行poll方法时超时了 并且业务线程缓存队列里没有要执行的任务时 则退出循环 也就是返回要执行的task为空 工作线程就会销毁

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