并发系列(一)——线程池源码(ThreadPoolExecutor类)简析

前言

  本文主要是结合源码去线程池执行任务的过程,基于JDK 11,整个过程基本与JDK 8相同。

  个人水平有限,文中若有表达有误的,欢迎大伙留言指出,谢谢了!

一、线程池简介

  1.1 使用线程池的优点

    1)通过复用已创建的线程,降低资源的消耗(线程的创建/销毁是要消耗资源的)、提高响应速度;

    2)管理线程的个数,线程的个数在初始化线程池的时候指定;

    3)统一管理线程,比如停止,stop()方法;

  1.2 线程池执行任务过程

    线程池执行任务的过程如下图所示,主要分为以下4步,其中参数的含义会在后面详细讲解:

    1)判断工作的线程是否小于核心线程数据(workerCountOf(c) < corePoolSize),若小于则会新建一个线程去执行任务,这一步仅仅的是根据线程个数决定;

    2)若核心线程池满了,就会判断线程池的状态,若是running状态,则尝试加入任务队列,若加入成功后还会做一些事情,后面详细说;

    3)若任务队列满了,则加入失败,此时会判断整个线程池线程是否满,若没有则创建非核心线程执行任务;

    4)若线程池满了,则根据拒绝测试处理无法执行的任务;

    整体过程如下图:

二、ThreadPoolExecutor类解析

  2.1 ThreadPoolExecutor的构造函数

    ThreadPoolExecutor类一共提供了4个构造函数,涉及5~7个参数,下面就5个必备参数的构造函数进行说明:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

    1)corePoolSize :初始化核心线程池中线程个数的大小;

    2)maxmumPoolSize:线程池中线程大小;

    3)keepAliveTime:非核心线程的超时时长;

      非核心线程空闲时常大于该值就会被终止。

    4)unit :keepAliveTime的单位,类型可以参见TimeUnit类;

    5)BlockingQueue workQueue:阻塞队列,维护等待执行的任务;

  2.2  私有类Worker

    在ThreadPoolExecutor类中有两个集合类型比较重要,一个是用于放置等待任务的workQueue,其类型是阻塞对列;一个是用于用于存放工作线程的works,其是Set类型,其中存放的类型是Worker。

    进一步简化线程池执行过程,可以理解为works中的工作线程不停的去阻塞对列中取任务,执行结束,线程重新加入大works中。

    为此,有必要简单了解一下Work类型的组成。

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /** Thread this worker is running in.  Null if factory fails. */
        //工作线程,由线程的工厂类初始化
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;
        //不可重入的锁
        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        .......
    }

    Worker类继承于队列同步器(AbstractQueueSynchronizer),队列同步器是采取锁或其他同步组件的基础框架,其主要结构是自旋获取锁的同步队列和等待唤醒的等待队列,其方法因此可以分为两类:对state改变的方法 和 入、出队列的方法,即获取获取锁的资格的变化(可能描述的不准确)。关于队列同步器后续博客会详细分析,此处不展开讨论。

    Work类中通过CAS设置状态失败后直接返回false,而不是判断当前线程是否已获取锁来实现不可重入的锁,源码注释中解释这样做的原因是因为避免work tash重新获取到控制线程池全局的方法,如setCorePoolSize。

  2.3  拒绝策略类

    ThreadPoolExecutor的拒绝策略类是以私有类的方式实现的,有四种策略:

    1)AbortPolicy:丢弃任务并抛出RejectedExecutionException异常(默认拒绝处理策略)。

      2)DiscardPolicy:抛弃新来的任务,但是不抛出异常。

      3)DiscardOldestPolicy:抛弃等待队列头部(最旧的)的任务,然后重新尝试执行程序(失败则会重复此过程)。

      4)CallerRunsPolicy:由调用线程处理该任务。

    其代码相对简单,可以参考源码。

三、任务执行过程分析

  3.1 execute(Runnable)方法

    execute(Runnable)方法的整体过程如上文1.2所述,其实现方式如下:

public void execute(Runnable command) {
        //执行的任务为空,直接抛出异常
        if (command == null)
            throw new NullPointerException();
        //ctl是ThreadPoolExecutor中很关键的一个AtomicInteger,主线程池的控制状态
        int c = ctl.get();
        //1、判断是否小于核心线程池的大小,若是则直接尝试新建一个work线程
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //2、大于核心线程池的大小或新建work失败(如创建thread失败),会先判断线程池是否是running状态,若是则加入阻塞对列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //重新验证线程池是否为running,若否,则尝试从对列中删除,成功后执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //若线程池的状态为shutdown则,尝试去执行完阻塞对列中的任务
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //3、新建非核心线程去执行任务,若失败,则采取拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

  3.2 addWorker(Runnable,boole)方法

    execute(Runnable)方法中,新建(非)核心线程执行任务主要是通过addWorker方法实现的,其执行过程如下:

private boolean addWorker(Runnable firstTask, boolean core) {
        //此处反复检查线程池的状态以及工作线程是否超过给定的值
        retry:
        for (int c = ctl.get();;) {
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;

            for (;;) {
            //核心和非核心线程的区别
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateAtLeast(c, SHUTDOWN))
                    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);
            //通过工厂方法初始化,可能失败,即可能为null
            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 c = ctl.get();
                    //线程池处于running状态
                    //或shutdown状态但无需要执行的task,个人理解为用于去阻塞队列中取任务执行
                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && 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 {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //执行任务,这里会执行thread的firstTask获取阻塞对列中取任务
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
            //开始失败,则会从workers中删除新建的work,work数量减1,尝试关闭线程池,这些过程会获取全局锁
                addWorkerFailed(w);
        }
        return workerStarted;
    }

  3.3  runWorker(this) 方法

     在3.2 中当新建的worker线程加入在workers中成功后,就会启动对应任务,其调用的是Worker类中的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()循环中,前者是新建线程执行firstTask,对应线程个数小于核心线程和阻塞队列满的情况,
        //getTask()则是从阻塞对列中取任务执行
            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
                //仅线程池状态为stop时,线程响应中断,这里也就解释了调用shutdown时,正在工作的线程会继续工作
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    try {
                    //执行任务
                        task.run();
                        afterExecute(task, null);
                    } catch (Throwable ex) {
                        afterExecute(task, ex);
                        throw ex;
                    }
                } finally {
                    task = null;
                    //完成的个数+1
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            //处理后续工作
            processWorkerExit(w, completedAbruptly);
        }
    }

   3.4 processWorkerExit(Worker,boole)方法

    当任务执行结果后,在满足一定条件下会新增一个worker线程,代码如下:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
        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
            }
            //执行完任务后,线程重新加入workers中
            addWorker(null, false);
        }
    }

  至此,线程池执行任务的过程分析结束,其他方法的实现过程可以参考源码。

 

Ref:

[1]http://concurrent.redspider.group/article/03/12.html

[2]《Java并发编程的艺术》

 

posted @ 2020-06-15 00:27  王大咩的图书馆  阅读(328)  评论(0编辑  收藏  举报