ThreadPoolExecutor源码分析

来源:互联网 发布:mac 你没有权限文件夹 编辑:程序博客网 时间:2024/05/21 19:25

一、线程和任务的概念

首先区分概念,任务和线程。可以简单理解为任务为Runnable,线程为Thread。ThreadPoolExecutor内部维持的是线程池,因为创建线程比较耗时耗资源。而内部维护任务使用的是BlockingQueue。

二、ThreadPoolExecutor源码

2.1 ctl变量

ThreadPoolExecutor中有一个ctl的变量,声明如下:

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctl是AtomicInteger的,所以是线程安全的。ctl维护两个概念上的参数:workCount和runState。workCount表示有效的线程数量,runState表示线程池的运行状态。运行状态只要有五个,分别是RUNNING、SHUTDOWN、STOP、TIDYING和TERMINATED。AtomicInteger是一个32位的整数,为了将状态和数量放在一起,所以高3位用于表示表示状态,低29位表示数量。下面是状态和一些参数定义:

 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;    private static final int TIDYING    =  2 << COUNT_BITS;    private static final int TERMINATED =  3 << COUNT_BITS;

下面介绍各个线程池各个状态的含义:

RUNNING
接受新任务并且处理已经进入队列的任务
SHUTDOWN
不接受新任务,但是处理已经进入队列的任务
STOP
不接受新任务,不处理已经进入队列的任务,并且中断正在执行的任务
TIDYING
所有任务执行完成,workerCount为0。线程转到了状态TIDYING会执行terminated()钩子方法
TERMINATED
terminated()已经执行完成
状态之间可以相互转换

RUNNING -> SHUTDOWN
调用了shutdown()方法
(RUNNING 或 SHUTDOWN) -> STOP
调用了shutdownNow()
SHUTDOWN -> TIDYING
当队列和线程池为空
STOP -> TIDYING
当线程池为空
TIDYING -> TERMINATED
当terminated()钩子方法执行完成
为了从clt中获取各部分的值,提供了如下方法:

 private static int runStateOf(int c)     { return c & ~CAPACITY; }    private static int workerCountOf(int c)  { return c & CAPACITY; }    private static int ctlOf(int rs, int wc) { return rs | wc; }

2.2 ThreadPoolExecutor构造方法

ThreadPoolExecutor有几个构造方法,构造方法中有几个参数,分别是corePoolSize、maximunPoolSize、keepAliveTime、unit、workQueue、threadFactory和handler。下面分别介绍这个几个参数:
corePoolSize
核心线程的数量。默认是没有超时的,也就是说就算线程闲置,也不会被处理。但是如果设置了allowCoreTimeOut为true,那么当核心线程闲置时,会被回收。
maximumPoolSize
最大线程池尺寸,被CAPACITY限制(2^29-1)。
keepAliveTime
闲置线程被回收的时间限制
unit
keepAliveTime的单位
workQueue
用于存放任务的队列
threadFactory
创建线程的工厂类
handler
当任务执行失败时,使用handler通知调用者

2.3 ThreadPoolExecutor流程

当创建好一个ThreadPoolExecutor对象后,调用execute(Runnable r)方法执行任务。下面是execute方法的实现:

 public void execute(Runnable command) {        //检查command不能为null        if (command == null)            throw new NullPointerException();        int c = ctl.get();        //如果当前线程小于corePoolSize        if (workerCountOf(c) < corePoolSize) {            //如果添加Worker线程成功,则返回            if (addWorker(command, true))                return;            c = ctl.get();        }        //如果当前正在运行阶段并且可以将线程入队        if (isRunning(c) && workQueue.offer(command)) {            //再次检查ctl状态            int recheck = ctl.get();            //如果不在运行状态了,那么就从队列中移除任务            if (! isRunning(recheck) && remove(command))                reject(command);            //如果在运行阶段,但是Worker数量为0,调用addWorker方法            else if (workerCountOf(recheck) == 0)                addWorker(null, false);        }        //如果不能入队,且不能创建Worker,那么reject        else if (!addWorker(command, false))            reject(command);    }

从注释中可以看到execute方法分为三步:
(1)如果小于corePoolSize的线程正在运行,那么创建一个核心线程,并将任务作为核心线程的第一个任务
(2)如果一个任务可以加入到队列中,然后需要在此检查是否需要新建一个线程(因为可能存在一个线程在上次检查完之后被回收了)或者因为线程池停止了。所以需要再次检查状态,如果不在RUNNING状态并且能够成功移除任务的话,那么调用reject方法,否则就调用addWorker(null,false)方法。
(3)如果任务不能放入队列,会首先尝试添加一个新线程(非核心线程)。如果失败,则调用reject方法。
首先看reject方法,

  /**     * Invokes the rejected execution handler for the given command.     * Package-protected for use by ScheduledThreadPoolExecutor.     */    final void reject(Runnable command) {        handler.rejectedExecution(command, this);    }

reject方法会调用handler的rejectedExecution(command,this)方法。handler是RejectedExecutionHandler接口,默认实现是AbortPolicy,下面是AbortPolicy的实现:

/**     * A handler for rejected tasks that throws a     * {@code RejectedExecutionException}.     */    public static class AbortPolicy implements RejectedExecutionHandler {        /**         * Creates an {@code AbortPolicy}.         */        public AbortPolicy() { }        /**         * Always throws RejectedExecutionException.         *         * @param r the runnable task requested to be executed         * @param e the executor attempting to execute this task         * @throws RejectedExecutionException always         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {            throw new RejectedExecutionException("Task " + r.toString() +                                                 " rejected from " +                                                 e.toString());        }    }

可以看到rejectedExecution方法就是抛出一个异常。
execute方法中主要使用到addWorker方法,addWorker方法用于创建线程,并且通过core参数表示该线程是否是核心线程,如果返回true则表示创建成功,否则失败。addWorker的代码如下所示:

 private boolean addWorker(Runnable firstTask, boolean core) {        //外循环死循环        retry:        for (;;) {            int c = ctl.get();            //得到运行状态            int rs = runStateOf(c);            // 检查状态            if (rs >= SHUTDOWN &&                ! (rs == SHUTDOWN &&                   firstTask == null &&                   ! workQueue.isEmpty()))                return false;            //状态符合跳球,死循环            for (;;) {                int wc = workerCountOf(c);                //如果worker数量超过了容量或者超过了corePoolSize或者maximumPoolSize,直接返回false                if (wc >= CAPACITY ||                    wc >= (core ? corePoolSize : maximumPoolSize))                    return false;                //如果成功将worker数+1,那么跳出外循环                if (compareAndIncrementWorkerCount(c))                    break retry;                //否则,重新读取ctl                c = ctl.get();  // Re-read ctl                if (runStateOf(c) != rs)                    continue retry;                // else CAS failed due to workerCount change; retry inner loop            }        }        //添加Worker        boolean workerStarted = false;        boolean workerAdded = false;        Worker w = null;        try {            //以firstTask作为Worker的第一个任务创建Worker            w = new Worker(firstTask);            final Thread t = w.thread;            if (t != null) {                //对整个线程池加锁                final ReentrantLock mainLock = this.mainLock;                mainLock.lock();                try {                    //再次检查ctl状态                    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 {                    mainLock.unlock();                }                if (workerAdded) {                    t.start();                    workerStarted = true;                }            }        } finally {            if (! workerStarted)                addWorkerFailed(w);        }        return workerStarted;    }

首先是两个死循环,外循环主要检查线程池运行状态,内循环检查workerCount之后再检查运行状态。下面简单分析一下哪些情况下才可以进入到内循环,否则就直接返回false了。下面是可以进入到内循环的情况:
(1)rs>=SHUTDOWN为false,即线程池处于RUNNING状态
(2)rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()这个条件为true,也就意味着三个条件同时满足,即线程池状态为SHUTDOWN且firstTask为null且队列不为空,这种情况为处理队列中剩余任务。上面提到过当处于SHUTDOWN状态时,不接受新任务,但是会处理完队列里面的任务。如果firstTask不为null,那么就属于添加新任务;如果firstTask为null,并且队列为空,那么就不需要再处理了。
当进入到内循环后,会首先获取当前运行的线程数量。首先判断当前运行线程数量是否大于等于CAPACITYA(2^29-1),其次根据是否是核心线程与corePoolSize或者maximumPoolSize比较。所以线程的数量不会超过CAPACITY和maximumPoolSize的较小值。如果数量符合条件,那么就让ctl加1,然后跳出外部循环。如果线程数量达到了最大,那么回再判断当前状态,如果状态和之前的不一致了,那么继续外循环。下面是可以跳出外循环的情况:
(1)如果是核心线程,当前线程数量小于CAPACITY和corePoolSize中的较小值
(2)如果是非核心线程,当前线程数量小于CAPACITY和maximumPoolSize中的较小值。
一旦跳出外循环,表示可以创建创建线程,这里具体是Worker对象,Worker实现了Runnable接口并且继承AbstractQueueSynchronizer,内部维持一个Runnbale的队列。try块中主要就是创建Worker对象,然后将其保存到workers中,workers是一个HashSet,表示工作线程的集合。然后如果添加成功,则开启Worker所在的线程。如果开启线程失败,则调用addWorkerFailed方法,addWokerFailed用于回滚worker线程的创建。下面是addWorkerFailed的实现:

    private void addWorkerFailed(Worker w) {        //对整个线程成绩加锁        final ReentrantLock mainLock = this.mainLock;        mainLock.lock();        try {            //移除Worker对象            if (w != null)                workers.remove(w);            //减小worker数量            decrementWorkerCount();            //检查termination状态            tryTerminate();        } finally {            mainLock.unlock();        }    }

从代码中可以看出,addWorkerFailed首先从workers集合中移除线程,然后将wokerCount减1,最后检查终结。下面是tryTerminate的实现,该方法用于检查是否有必要将线程池状态转移到TERMINATED。

final void tryTerminate() {        for (;;) {            int c = ctl.get();            if (isRunning(c) ||                runStateAtLeast(c, TIDYING) ||                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))                return;            if (workerCountOf(c) != 0) { // Eligible to terminate                interruptIdleWorkers(ONLY_ONE);                return;            }            final ReentrantLock mainLock = this.mainLock;            mainLock.lock();            try {                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        }    }

tryTerminate内部是一个死循环,首先判断状态,下面是跳出循环的情况:
(1)线程池处于RUNNING状态
(2)线程池状态处于TIDYING状态
(3)线程池状态处于SHUTDOWN状态并且队列不为空
如果不满足上述的情况,那么目前状态属于SHUTDOWN切队列为空,或者状态属于STOP,那么调用interruptIdleWorkers方法停止一个Worker线程,然后退出。
接下来如果没有退出循环的话,那么就首先将状态设置成TIDYING,然后调用terminated方法,最后设置状态为TERMINATED。terminated方法是个空实现,用于当线程池终结时处理一些事情。
下面看Worker的实现,

    private final class Worker        extends AbstractQueuedSynchronizer        implements Runnable    {        private static final long serialVersionUID = 6138294804551838833L;        /** 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;        /**         * Creates with given first task and thread from ThreadFactory.         * @param firstTask the first task (null if none)         */        Worker(Runnable firstTask) {            setState(-1); // inhibit interrupts until runWorker            this.firstTask = firstTask;            this.thread = getThreadFactory().newThread(this);        }        /** Delegates main run loop to outer runWorker  */        public void run() {            runWorker(this);        }        // Lock methods        //        // The value 0 represents the unlocked state.        // The value 1 represents the locked state.        protected boolean isHeldExclusively() {            return getState() != 0;        }        protected boolean tryAcquire(int unused) {            if (compareAndSetState(0, 1)) {                setExclusiveOwnerThread(Thread.currentThread());                return true;            }            return false;        }        protected boolean tryRelease(int unused) {            setExclusiveOwnerThread(null);            setState(0);            return true;        }        public void lock()        { acquire(1); }        public boolean tryLock()  { return tryAcquire(1); }        public void unlock()      { release(1); }        public boolean isLocked() { return isHeldExclusively(); }        void interruptIfStarted() {            Thread t;            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {                try {                    t.interrupt();                } catch (SecurityException ignore) {                }            }        }    }

Worker继承自AbstractQueuedSynchronizer并实现Runnbale接口。AbstractQueuedSynchronizer提供了一个实现阻塞锁和其他同步工具,比如信号量、事件等依赖于等待队列的框架。Woerker的构造方法中会使用threadFactory构造线程变量并持有,run方法调用了runWorker方法,将线程委托给主循环线程。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 {            //当任务不为null时            while (task != null || (task = getTask()) != null) {                //对Worker加锁                w.lock();                //如果线程池停止了,那么中断线程                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);        }    }

runWoker方法主要不断从队列中取得任务并执行。首先获得Worker所在的线程,在addWorker中获得Worker的Thread变量并调用start方法,所以Worker是运行在Worker的Thread中,而thread变量是通过threadFactory创建的。可以看到首先获取Worker的firstTask对象,该对象有可能为空,初始时对于核心线程不为空,但是对于非核心线程就为空,下面是一个循环,跳出循环的条件为task==null&&(task=getTask())==null,也就是说当没有任何任务的时候,就跳出循环了,跳出循环也就意味着Worker的run方法执行结束,也就意味着线程结束;否则会一直尝试着从队列中获取任务来执行,getTask会阻塞,一旦获取到任务,就对Worker加锁,然后判断状态,如果状态处于STOP状态及之上,就不处理任务了;否则处理任务,在处理任务之前,首先会调用beforeExecute,然后调用Runnbale方法的run方法,最后调用afterExecute,其中beforeExecute和afterExecute都是空实现,继承ThreadPoolExecutor时可以实现,在每个任务运行之前和之后做一些处理工作。一旦一个任务执行完毕后,将task置为null,然后继续尝试从队列中取出任务。下面看一下getTask方法的实现:

 private Runnable getTask() {        boolean timedOut = false; // Did the last poll() time out?        for (;;) {            int c = ctl.get();            int rs = runStateOf(c);            // 必要时检查队列是否为空            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {                decrementWorkerCount();                return null;            }            int wc = workerCountOf(c);            // 是否允许线程超时            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;            //如果worker数量大于maximumPoolSize或者允许超时            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;            }        }    }

getTask从队列中取出任务;但是在以下几种情况下会返回null,上面说过如果返回null也就标识了runWorker中循环跳出,上面说过,runWorker中的循环跳出意味着Worker线程执行完毕会回收,所以调用了decrementWorkerCount将Worker数量减1。下面返回null的情况
(1)由于调用了setMaximumPoolSize导致Worker的数量超过maximumPoolSize
(2)线程池处于STOP状态,STOP状态不再处理队列中的任务
(3)线程池处于SHUTDOWN并且queue为空。SHUTDOWN状态仍然处理已经在队列中的任务,但是如果queue为空,自然就不再处理了
(4)Worker在等待队列时超时
getTask内部依然是一个死循环,首先依然是判断状态,如果状态是STOP及以上,那么返回null;如果状态是SHUTDOWN且队列为空,那么也返回null。这对应于情况2和3。
接下来是比较Worker的数量,首先获取Worker的数量以及是否需要超时标志,如果设置了allCoreThreadTimeOut为true,那么就意味着所以线程都得检验超时;而如果没有设置为true,那么只需要在Worker数量超过corePoolSize时检查超时。接下来是判断数量是否超过maximumPoolSize,如果超过了,则需要结束多余的Worker;如果超时了并且有时间限制,也需要停止线程。如果没有进入到if语句中,那么将会尝试从队列中获取任务,如果需要有时间限制,那么就调用workQueue的poll方法,如果没有则调用take方法,如果可以从队列中取到任务,那么就返回任务交由runWorker中去执行;但是如果返回失败,那么需要设置timeOut为true,这样在下一次进入循环时,会清除一个Worker。
上面是ThreadPoolExecutor调用execute方法提交任务后的执行流程,下面总结一下:
1. 当Worker数量小于corePoolSize时,新建核心Worker,并将任务作为firstTask参数传入,然后返回;由于runWorker方法中firstTask不为null,所以核心线程在第一次进入循环时会将firstTask执行完成后,再进入循环时getTask时会阻塞,因为此时队列里面任务为空
2. 如果Worker数量超过corePoolSize,那么会首先将任务加入队列;如果可以成功加入队列,那么就再判断是否还在运行状态,如果不在运行状态,那么就从队列中删除任务并且调用reject方法;否则如果因为Worker数量为0,那么就创建一个非核心线程处理队列中的任务。
3. 如果2中由于队列已满不能加入队列,那么就尝试着开启一个非核心线程,如果开启非核心线程失败了,那么就调用reject处理;否则就等待着非核心线程从队列中取数据。
addWorker方法中会两个死循环,外循环检查线程池状态是否还可以接受新任务;内循环根据是否是核心线程与corePoolSize或maximumPoolSize比较,如果数量符合则创建线程,否则添加失败。

三、常见线程池

创建线程池一般都通过Executors的工厂方法创建线程,一般有四种线程,分别是FixedSizeThreadPoolc、SingleThreadPool、CachedThreadPool和ScheduledThreadPool。

3.1 FixedSizeThreadPool

FixedSizeThreadPoolExecutor只有核心线程,没有非核心线程,并且核心线程的数量固定。下面是创建该线程池的方法:

 public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }

可以看到corePoolSize和maximumPoolSize均为nThreads参数,并且没有超时且队列是没有边界的。所以该线程池一旦开启,最多会有nThreads个线程,且线程一旦创建,就不会销毁。只要有任务提交,就会添加给核心线程或加入队列。

3.2 SingleThreadPool

SingleThreadPool创建一个Worker线程操作一个无边界的队列。如果使用该线程池,那么所有提交的任务将会按照顺序被执行。

 public static ExecutorService newSingleThreadExecutor() {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,                                    0L, TimeUnit.MILLISECONDS,                                    new LinkedBlockingQueue<Runnable>()));    }

3.3 CachedThreadPool

CachedThreadPool只要需要新线程就会创建线程,如果之前创建的线程还可以复用,那么就会复用之前的线程。

   public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>());    }

从构造方法中可以看出,corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,超时为60s且队列为SynchronousQueue。从前面的分析我们知道,线程池中线程的最大数量为CAPACITY,所以就算这边设置了Integer.MAX_VALUE,但是最大数量也只能达到2^29-1个线程。SynchronousQueue不会持有任务,一旦拥有任务就会将任务交给线程。所以说会不断创建线程,而如果线程没有销毁的话,就会从调用getTask尝试从队列中获取任务,如果长时间没有新任务,那么之前的线程会由于超时而销毁;而如果在这期间新加了任务,那么getTask就可以获取到任务,那么之前创建的线程也就可以得到复用。

3.4 ScheduledThreadPool

ScheduledThreadPool核心线程数量固定,非核心线程数量为Integer.MAX_VALUE,该线程池主要用于执行周期性的任务或在延时一段时间后执行任务。

  public static ScheduledExecutorService newScheduledThreadPool(            int corePoolSize, ThreadFactory threadFactory) {        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);    }

四、总结

1. ThreadPoolExecutor构造器中各个参数的含义

在addWorker、getTask等方法中都需要使用到构造器中的参数,主要包括四部分,第一部分是线程数量,corePoolSize和maximumPoolSize;第二部分是超时,unit和keepAliveTime;第三部分是创建运行Worker的线程,即threadFactory;第四部分是RejectedExecutionHandler,即handler。
有些线程池会设置maximumPoolSize为Integer.MAX_VALUE,但是由于高三位需要作为线程池的状态,所以线程池中线程的最大数量为CAPACITY

2. ThreadPoolExecutor各个状态的含义以及状态转换

ThreadPoolExecutor的状态保存在ctl变量的高三位,具有五种状态,分别是RUNNING、SHUTDOWN、STOP、TIDYING和TERMITERNED。理解每个状态下线程池对任务和线程的操作,才能清楚在各个方法中为什么那么处理。

3. 执行流程

执行流程分为三步,
1. 如果当前线程数小于corePoolSize,那么创建线程
2. 如果当前线程数大于等于corePoolSize,那么将任务加入队列;如果成功加入,那么就等待线程的getTask获取到任务再去执行;
3. 如果第2步中加入队列失败,那么尝试开启线程。如果当前线程数小于maximumPoolSize,那么创建线程成功,如果大于等于maximumPoolSize,那么创建线程失败。
在上面为了讲解,区分出核心线程和非核心线程的区别,但是其实都一样,只不过是一个有初始的任务,一个firstTask为null,一旦当核心线程执行完初始的任务后,它就变得和非核心线程一样。如果设置了超时,那么并不会因为它是所谓的“核心线程”而不销毁,那么个时候所有线程都一样,一旦哪个线程阻塞在getTask那儿,就可能因为超时而销毁。
在整个执行流程中,各个方法中会有多个死循环,要清楚在哪些状态下会跳出那些死循环。

1 0