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线程集合执行任务或者从任务队列获取任务,就相当于消费者消费.

相关推荐
续写少年!18 分钟前
自定义注解
java·开发语言
请你打开电视看看21 分钟前
结构型模式-桥接模式
java·python·桥接模式
苹果酱056727 分钟前
通俗理解人工智能、机器学习和深度学习的关系
java·开发语言·spring boot·mysql·中间件
Yanbin_Q38 分钟前
Java 使用堆外内存(off-heap memory) 作为缓存
java·开发语言·缓存
码上有前1 小时前
【51-60期】深入解析Java面试问题:从高并发到性能调优的最佳实践
java·开发语言·面试
秋凉 づᐇ1 小时前
数据结构(汇总)
java·数据结构·算法
wjs20241 小时前
基于Java的Nacos云原生动态服务发现、配置和服务管理平台设计源码
java·nacos·服务发现·配置管理·云原生平台
chyun20112 小时前
代码精简之路-责任链模式
java·设计模式
用户99045017780092 小时前
真需求永远是第一位
后端
孟林洁2 小时前
Jenkins流水线 & Allure & JUnit5 自动化测试
java·运维·jenkins