线程池(二)-- 源码分析

178 阅读8分钟

前言

前面一篇文章讲述了线程池的处理任务的流程以及一些基本概念,接下来对线程池源码进行分析;

正文

execute方法:

    public void execute(Runnable command) {
        if (command == null)// 1.判断任务是否为空
            throw new NullPointerException();
        int c = ctl.get();// 2.获取线程池ctl属性,根据此属性对线程池的一些其他属性进行判断
        if (workerCountOf(c) < corePoolSize) {// 3. 线程数小于核心线程数
            if (addWorker(command, true))// 3.1添加核心线程
                return;
            c = ctl.get();// 3.2添加失败则重新获取此属性
        }
        if (isRunning(c) && workQueue.offer(command)) {// 4. 判断线程状态并将任务添加进队列(这里进行了双重检验)
            int recheck = ctl.get();//4.1重新获取ctl属性值
            if (! isRunning(recheck) && remove(command))//4.2 如果线程池状态没有在运行中且移除任务失败
                reject(command);// 4.2.1对该任务执行拒绝策略
            else if (workerCountOf(recheck) == 0)// 4.3线程池中的线程数为0
                addWorker(null, false);// 4.3.1添加一个任务为空非核心线程
        }
        else if (!addWorker(command, false))// 5. 添加工作任务
            reject(command);// 5.1 添加失败则执行拒绝策略
    }

拒绝策略

拒绝策略方法是reject方法:

    final void reject(Runnable command) {
        handler.rejectedExecution(command, this);
    }

该方法最终会调用handler的rejectedExecution方法;handler是创建线程池时传入的拒绝策略参数,该参数需要实现RejectedExecutionHandler接口,而在ThreadPoolExecutor类中分别定义了四种拒绝策略:CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldestPolicy;而线程池默认情况下是使用的AbortPolicy来作为拒绝策略方法;而AbortPolicy实现的接口的代码如下:

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }

其实就是抛出异常,所以说线程池的拒绝策略为默认抛出异常;

说明: execute 方法主要是判断进入的任务进入哪一个流程(核心线程,队列,拒绝策略)

addWorker 方法


 private boolean addWorker(Runnable firstTask, boolean core) {
        retry: // 1. 标记循环返回点
        for (;;) { // 2. 循环
            int c = ctl.get();
            int rs = runStateOf(c);
            if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) // 2.1   判断线程池状态(运行状态,队列,任务)
                return false;
            for (;;) {//2.2  循环
                int wc = workerCountOf(c); // 2.2.1 获取线程池当前工作线程数量
                if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))// 2.2.2 判断是否超过线程池规定数量
                    return false;
                if (compareAndIncrementWorkerCount(c)) // 2.2.3 cas方式修改ctl属性
                    break retry; // 2.2.3.1 成功则退出至标记的点,然后再进入循环后的步骤
                c = ctl.get();  // Re-read ctl 重新获取ctl
                if (runStateOf(c) != rs) // 2.2.4
                    continue retry; // 判断线程状态,退至循环标记点,再次进入循环
            }
        }
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 创建一个Worker并将任务添加进去
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                // 可重入锁
                final ReentrantLock mainLock = this.mainLock;
                // 重入锁,保证线程安全
                mainLock.lock();
                try {
                    // 获取线程池运行状态
                    int rs = runStateOf(ctl.get());
                    // 判断线程状态,判断任务对象是否为空
                    if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {
                         // 判断线程是否在运行中
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        // 添加工作线程(set集合)
                        workers.add(w);
                        // 记录添加的线程数量
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    // 解锁
                    mainLock.unlock();
                }
                if (workerAdded) {
                    // 启动worker中线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            判断worker是否添加成功
            if (! workerStarted)
                // 添加失败
                addWorkerFailed(w);
        }
        return workerStarted;
    }

该方法使用CAS的方式对工作线程数量(ctl)进行改变,也就是最上边的那段使用了CAS进行并发控制的代码段;

Work类

Work类是ThreadPoolExecutor的一个内部类;继承了AbstractQueuedSynchronizer;

Work构造方法


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

该构造方法传入一个Runnable的执行任务,使用ThreadPoolExecutor类的线程工厂来创建一个线程来执行任务;

Work的run方法

        /** Delegates main run loop to outer runWorker  */
        public void run() {
            runWorker(this);
        }

该Work的线程将会调用runWorker方法

runWorker方法

在addWorker方法中已经启动了worker中的线程,现在runWorker就是执行任务

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) { // 判断worker中的任务,以及队列中是否存在任务
                w.lock(); // aqs锁,防止线程正在执行过程中被线程池中断
                // 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 {
            // 该线程执行完毕,对worker进行标记并清理
            processWorkerExit(w, completedAbruptly);
        }
    }

该方法主要负责执行任务,并且在执行任务前后分别进行了处理,可以在此自定义一些处理,比如任务执行时间,任务执行情况,线程池状态等等; 还有一点就是会不断循环的去获取队列中的任务,直到获取不到任务为止,最后再对worker进行处理

completedAbruptly:这个属性初始值为true,在任务执行完成后会改变为false,除非任务非正常执行完成,跳过了改变为false的哪一步;

最后一个finally中的代码只有非核心线程(非核心worker)才会进入,核心的worker会在getTask中阻塞等待获取队列中的任务;

getTask方法

runWorker方法通过该方法从队列中获取任务,代码如下:

    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?
            // 判断是核心线程还是非核心线程并且判断核心线程是否设置等待超时;allowCoreThreadTimeOut默认false
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            // 判断当前worker是否要被回收,主要目的是修改ctl值(减一)  1. 疑点
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 修改ctl值,并返回null
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                // 从队列中获取任务 ,非核心线程就是要设置等待超时,核心线程就阻塞获取队列中的任务
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                // 如果获取的任务不为空,则返回
                if (r != null)
                    return r;
                // 设置超时时间为true
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

说明: 疑点1:

  1. 这段代码的主要的任务就是将ctl减一(相当于就是任务执行完成,而且队列也为空,),但是判断的条件就比较多了,而使用的是CAS,这里就对并发进行了控制,始终保证只有一个线程会执行ctl减1,也就是线程回收;
  2. 判断条件主要就是工作线程是否大于最大线程数,是否超时判断,队列是否为空,工作线程是否大于1
  3. 这个点我能想到的触发场景就是:第一次从队列获取数据,但是获取为空,然后循环,在进入第二次循环后,队列判断前有新任务进入了队列,这个点才会被触发

其它: 如果是核心线程进入该方法,会根据allowCoreThreadTimeOut属性来判断是使用超时策略(poll)还是阻塞式(take)获取任务, 在没有设置allowCoreThreadTimeOut的情况下,且队列中没有任务,那么核心线程就会一直阻塞在这个位置, 如果设置了,那么该线程就会和其它非核心线程执行流程一样;

非核心则采取超时策略;超时过后返回null;

创建线程池时设置的超时策略就在这个方法中的获取队列任务时体现;

结合runWorker,getTask,processWorkerExit,即便是设置了allowCoreThreadTimeOut,如果队列不为空,则会一直循环的创建, 线程池会一直存活也在这个地方体现;

processWorkerExit 方法

源码:

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        // 判断worker是否异常中断
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 将worker中执行的任务数量添加到线程池completedTaskCount中
            completedTaskCount += w.completedTasks; 
            // 移除worker集合中移除当前worker
            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
            }
            // 使用新的worker替换
            addWorker(null, false);
        }
    }

这里主要是对worker进行回收,也就是worker执行到这个方法就完成了; 但是有个注意点:如果设置了allowCoreThreadTimeOut属性,所有的线程都会等待超时,并被回收;但是这时有任务进入队列(队列不为空), 但是又没有工作线程,这里则会重启一个线程来处理队列中的任务;

tryTerminate方法

源码:

    final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            // 判断线程池状态
            if (isRunning(c) ||                    // 排除Running状态
                runStateAtLeast(c, TIDYING) ||    // 排除TIDYING和TERMINATED
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) // 排除SHUTDOWN但是队列不为空
                return;
             // 判断是否有工作线程
            if (workerCountOf(c) != 0) { // Eligible to terminate
                // 中断所有线程,直到剩下一个线程为止
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 将线程池状态改为TIDYING
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        // 程序终止
                        terminated();
                    } finally {
                        // 修改线程池状态
                        ctl.set(ctlOf(TERMINATED, 0));
                        // 唤醒所有线程
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

这个方法很是关键:shutdown,shutdownNow,processWorkerExit等等方法均调用了该方法; 这三个方法通过不同的线程池状态进入,最后处理的结果也不相同;

shutdown方法

停止接收新的任务,队列中的任务还是会被执行;

源码:


    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 检查线程是否运行关闭
            checkShutdownAccess();
            //修改线程池状态
            advanceRunState(SHUTDOWN);
            // 关闭空闲线程()
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 尝试关闭线程池
        tryTerminate();
    }

这这个方法主要还是修改线程池状态,然后调用tryTerminate方法;

shutdownNow方法

源码:

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 检查线程是否运行关闭
            checkShutdownAccess();
            // 修改线程池状态
            advanceRunState(STOP);
            // 关闭空闲线程
            interruptWorkers();
            // 获取队列中的任务
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        // 尝试关闭线程池
        tryTerminate();
        // 返回队列中任务的集合
        return tasks;
    }

可以看出shutdownNow和shutdown的流程差不多,唯一不同的就是状态值和shutdownNow会返回队列中未执行的任务;

##interruptIdleWorkers方法

源码:


    private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
    }

    private void interruptIdleWorkers(boolean onlyOne) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                for (Worker w : workers) {
                    Thread t = w.thread;
                    if (!t.isInterrupted() && w.tryLock()) {
                        try {
                            // 中断线程
                            t.interrupt();
                        } catch (SecurityException ignore) {
                        } finally {
                            w.unlock();
                        }
                    }
                    if (onlyOne)
                        break;
                }
            } finally {
                mainLock.unlock();
            }
     }

注意这是两个方法;

这两个方法的主要作用就是中断worker集合中每个worker的Thread任务; 需要注意的是使用了Thread的interrupt方法;如果Thread正在执行任务中,那么这个方法会执行完成会关闭; 如果这个Thread在队列的take阻塞中,也会被中断掉;

最后

总结

参考

  1. 并发系列(6)之 ThreadPoolExecutor 详解
  2. ThreadPoolExecutor 详解
  3. 线程池技术之:ThreadPoolExecutor 源码解析