ThreadPoolExecutor源码解析

我们工作中肯定避免不了使用线程的情况,而常规的new一个线程的方式,在线程使用情况较多的场景下却是不太满足需求,一方面容易造成线程资源的耗费,一方面也是不方便统一管理无法复用,而线程池的出现就很好的解决了这些问题,其中使用最多的就是ThreadPoolExecutor。

开始源码之前,得先知道ThreadPoolExecutor的几种状态,ThreadPoolExecutor的状态和数量是通过一个Integer进行记录的,高3位记录了ThreadPoolExecutor的状态,其余的位数是用来记录线程数量。这个方式在java源码中很常见,他们很多时候都会根据位数去处理多个状态的情况,而不是重新在定义一个新的变量。

java 复制代码
    //记录当前线程状态
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    //掩码位数
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //线程最大个数
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    //接受新任务并且处理阻塞队列的任务
    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    //拒绝新任务但是处理阻塞队列里的任务
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //拒绝新任务并且抛弃阻塞队列里的任务,同时会中断正在处理的任务
    private static final int STOP       =  1 << COUNT_BITS;
    //所有任务都执行完,包括阻塞队列里的任务后当前线程池的活动线程为0
    private static final int TIDYING    =  2 << COUNT_BITS;
    //终止状态
    private static final int TERMINATED =  3 << COUNT_BITS;

查看构造函数

java 复制代码
     /**
     * 工作队列
     */
    private final BlockingQueue<Runnable> workQueue;
     /**
     * 线程池工厂
     */
    private volatile ThreadFactory threadFactory;
    /**
     * 饱和策略
     */
    private volatile RejectedExecutionHandler handler;
    /**
     * 空闲线程超时时间
     */
    private volatile long keepAliveTime;
    /**
     * 核心线程是否允许超时
     */
    private volatile boolean allowCoreThreadTimeOut;
    /**
     * 核心线程数
     */
    private volatile int corePoolSize;
    /**
     * 最大线程数
     */
    private volatile int maximumPoolSize;
    /**
     * 默认饱和策略处理器,默认是抛出饱和异常
     */
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
        
    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;
    }

工作上比较常用的应该是核心线程数,最大线程数和饱和策略的配置,要想让ThreadPoolExecutor使用不同的情况则需要配置适合的工作队列,各类型的线程池本质上来说还是队列的选型不同。

查看最核心的execute方法

java 复制代码
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);
    }
java 复制代码
    // 执行配置的饱和策略
    final void reject(Runnable command) {
        handler.rejectedExecution(command, this);
    }

整体看代码,会发现他虽然叫execute方法,但实际上他没有真正进行所谓的execute,而是根据条件判断是否需要添加新的woker。点开addWorker方法。

java 复制代码
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            //获取当前的线程池状态和数量
            int c = ctl.get();
            //获取线程池状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.

            if (
                // 线程池处于STOP,TIDYING,TERMINATED,SHUTDOWN状态时,都不能新增线程
                rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                //查看线程数
                int wc = workerCountOf(c);
                // 线程数超限制则禁止新增线程
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // cas增加线程数,成功则推出双循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //如果cas增加失败,则重新获取线程池状态
                c = ctl.get();  // Re-read ctl
                //线程池状态不一致,则到最外层循环,重新开始,一致则重新进行线程的增加
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        // cas新增线程个数成功
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //正式创建线程
            w = new Worker(firstTask);
            // 获取Worker的thread对象
            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());
                    // 线程池状态为RUNNING状态,或者线程池状态为SHUTDOWN,并且firstTask为null,则新增线程
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //判断当前线程是否已经启动
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        // 添加新的work线程到workers中
                        workers.add(w);
                        int s = workers.size();
                        //个数超出则创建失败
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 线程添加成功,则启动work线程
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            //如果线程启动失败,则删除work线程
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

代码有点长,甚至还有点老,现在大家应该都不会使用goto语法了,看一下提交记录,这时候感觉我小学是不是还没毕业。。。

回归正题,整体的addWorker还是很长的,所以按功能我们可以给他拆分一下。

java 复制代码
 retry:
        for (;;) {
            //获取当前的线程池状态和数量
            int c = ctl.get();
            //获取线程池状态
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.

            if (
                // 线程池处于STOP,TIDYING,TERMINATED,SHUTDOWN状态时,都不能新增有任务的线程,但可以新增空的线程
                rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                //查看线程数
                int wc = workerCountOf(c);
                // 线程数超限制则禁止新增线程
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // cas增加线程数,成功则推出双循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //如果cas增加失败,则重新获取线程池状态
                c = ctl.get();  // Re-read ctl
                //线程池状态不一致,则到最外层循环,重新开始,一致则重新进行线程的增加
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

这块主要是通过自旋,去增加线程数,如果ThreadPoolExecutor处于STOP,TIDYING,TERMINATED,SHUTDOWN状态时则会拒接添加新的线程,如果ThreadPoolExecutor的状态为RUNNING,则通过cas进行线程数的增加,成功则退出循环,如果线程数没有超过最大的数却还是失败,则会重新判断当前ThreadPoolExecutor的状态和刚开始的ThreadPoolExecutor的状态是否一致,不一致则会重新最外部的自旋,否则会继续cas增加线程数。 线程数增加成功后,就进入下面这部分,也就是正式创建worker。

java 复制代码
    // 独占锁
    private final ReentrantLock mainLock = new ReentrantLock();

    /**
     * woker集合
     */
    private final HashSet<Worker> workers = new HashSet<Worker>();
java 复制代码
        // woker启动是否失败
        boolean workerStarted = false;
        // woker添加是否成功
        boolean workerAdded = false;
        Worker w = null;
        try {
            //正式创建线程
            w = new Worker(firstTask);
            // 获取Worker的thread对象
            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());
                    // 线程池状态为RUNNING状态,或者线程池状态为SHUTDOWN,并且firstTask为null,则新增线程
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //判断当前线程是否已经启动
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        // 添加新的work线程到workers中
                        workers.add(w);
                        int s = workers.size();
                        //个数超出则创建失败
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 线程添加成功,则启动work线程
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            //如果线程启动失败,则删除work线程
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;

到这里整个worker就创建完成了,但是还有一个问题就是还是没有找到执行消费队列的方法。点开worker构造函数,你会发现,他这里创建线程时传入的runnable是自己,这就说明Worker继承了Runnable接口,那就简单了,找到run方法就行了。可以看到run方法调用了runWorker方法。

java 复制代码
        //woker构造函数
        Worker(Runnable firstTask) {
            //防止worker创建时呗中断
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
java 复制代码
        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

点开runworker

java 复制代码
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        //因为初始化worker的时候,会把当前线程的中断状态置为-1,所以要重置一下
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //如果任务不为空,或者从任务队列中获取到了任务,则执行任务
            while (task != null || (task = getTask()) != null) {
                w.lock();
                //线程池状态为stop当前线程未中断,则中断线程
                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;
                    //统计当前work完成了多少任务数
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            //清理work
            processWorkerExit(w, completedAbruptly);
        }
    }
java 复制代码
private void processWorkerExit(Worker w, boolean completedAbruptly) {
        //如果线程异常退出,则work线程数减1
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //统计整个线程池的完成的工作数
            completedTaskCount += w.completedTasks;
            // 删除当前的work线程,回收线程
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
        // 尝试设置线程池未Terminated状态
        tryTerminate();
        //获取当前线程池状态和数量
        int c = ctl.get();
        // 线程池状态为RUNNING,SHUTDOWN时,判断线程数是否小于核心线程数,小于则创建一个没有任务的work线程
        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);
        }
    }

总而言之,ThreadPoolExecutor实际就是一个生产消费者的模型,用户添加任务到线程池,相当于生产者生产元素,worker线程集合执行任务或者从任务队列获取任务,就相当于消费者消费.

相关推荐
一只叫煤球的猫8 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9658 小时前
tcp/ip 中的多路复用
后端
bobz9658 小时前
tls ingress 简单记录
后端
皮皮林5519 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友9 小时前
什么是OpenSSL
后端·安全·程序员
bobz96510 小时前
mcp 直接操作浏览器
后端
前端小张同学12 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook12 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康13 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在13 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net