从零开始的ThreadPoolExecutor源码解析


ThreadPoolExecutor在我们日常操作中经常使用,常见的那些使用基本使用方法和就不说了,这篇分析一下基本源码(其实是因为源码比较少)

1.基本参数

首先看一下定义的基本参数

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

    // Packing and unpacking ctl
    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; }

ctl : 这个是记录的当前的状态信息,是一个int类型,总计32位这。里分为两块内容,高3位记录的是当前的线程池状态,也就是下面的RUNNING,SHUTDOWN等,低29位记录的是当前运行的线程的个数。这个构建方式和我们常见的MeasureSpec类似。这里通过AtomicInteger(ctlOf(RUNNING, 0))设置了当前的默认状态RUNNING,以及线程个数0

COUNT_BITS : 位运算的偏移个数标记,Integer.SIZE32,所以这里是29

CAPACITY : 这个是容量的标记mask属性,就是1进行左移29位后减一,根据位运算的结果就是0001 1111 1111 1111 1111 1111 1111 1111总计29个1,方便后面进行&运算获取运行的线程个数

RUNNING : 正常运行状态,也是最初的状态
SHUTDOWN : 关闭状态,通过调用shutdown方法会设置此状态,shutdown方法并不会打断正在执行的线程
STOP : 停止状态,通过调用shutdownNow方法会设置此状态,shutdownNow会去打断正在执行的线程
TIDYING : 每当一个线程任务执行完毕后会调用tryTerminate这个方法,如果当前线程已经调用了shutdown等方法修改了状态,那么后面会再进行一系列判读,如果都满足就会使用CAS把当前的线程池状态修改成TIDYING,然后执行完terminated方法后会立刻再修改状态为最终的TERMINATED状态
TERMINATED : 终止状态,也是一个线程池的最后一个状态, 表面这个线程已经完全停止,里面的任务个数是0,并且也不再接受新的任务,其实SHUTDOWN 及后面的状态全都不再接受新的任务插入了

这几个状态是依次递增的,这个要记住,后面判断状态会用到这个特点

知道这些参数的意义,那么上面几个方法意义也就明确了
runStateOf : 获取传入参数的高3位的值,其实就是上面的几个状态
workerCountOf : 获取传入参数低29位的值,这里也就是活动的任务个数
ctlOf: 这个可以类比于MeasureSpecmakeMeasureSpec,根据两个值|运算计算新的值,这里一般是上面的状态(只有高3位有值)和任务个数(只有低29位有值)组成新的值

private final BlockingQueue<Runnable> workQueue;
private final ReentrantLock mainLock = new ReentrantLock();
private final Condition termination = mainLock.newCondition();
private final HashSet<Worker> workers = new HashSet<>();
private int largestPoolSize;
private long completedTaskCount;
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();

workQueue : 阻塞队列,当核心线程满了后,后面添加的任务会
mainLock : 可重入锁,用来做同步处理的
termination : ReentrantLock的条件处理策略Condition,类似有waitnotify;可以通过awaitsignal阻塞和唤醒指定的线程
workers : 当前执行的任务的集合,这个里面存储的Worker本身继承了AQS,同时实现了Runnable,也就是Worker本身既是任务执行者也可以当成锁来用,而且Worker内部会有循环取任务的处理,如果阻塞队列里有任务,一个Worker是可以执行多个任务的,并不一定执行一次就马上退出
largestPoolSize : 线程池中最大线程个数,这个记录的是线程中曾经出现过的最大的线程个数
completedTaskCount : 完成的任务个数,每个Worker都可能会完成多个任务,当Worker最终执行完关闭后会把其完成的任务个数进行累加,completedTaskCount是所有已完成的任务的总和
threadFactory : 线程工厂,就是执行任务的线程的生产工厂,所有的线程都是从这里产生的,最简单的我们可以通过new Thread去生产一个Thread对象
handler :拒绝策略,当所有的核心线程满了,队列满了,最大线程也满了时,再往线程池里放任务就会走这个策略,默认的是defaultHandler = new AbortPolicy(),即抛出异常
keepAliveTime : 保持存活的时长,当一个Worker执行完任务后最大等待时长,超过则关闭这个Worker,其实这个就是通过阻塞队列的poll(long timeout, TimeUnit unit)的阻塞超时时长进行控制的
allowCoreThreadTimeOut : 设置核心线程是否也使用超时,也就是上面的keepAliveTime,一般核心线程是不会关闭的,使用take方法阻塞获取任务,一直到有任务为止,设置这个后则和非核心线程一样通过poll(timeout,timeunit)方法一样超过时间则关闭
corePoolSize : 核心线程数,这个就很基础了,一个线程池创建优先都是核心线程池先创建,然后阻塞队列,然后才是非核心线程,这个应该都知道
maximumPoolSize : 最大线程个数,就是包括核心线程在内的最大个数了,核心线程之外的就是非核心线程了,一般核心线程不会被关闭,非核心线程任务执行完后超过一定时间就回收了

2.源码分析

线程池的入口是execute方法,从这里入手即可

    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);
    }

final void reject(Runnable command) {
		//这个handler默认不设置就是使用defaultHandler进行初始化
        handler.rejectedExecution(command, this);
 }

代码很好理解,当线程个数小于corePoolSize核心线程个数时,通过addWorker添加一个任务,addWoker下面会讲,这里的第二个参数指的是是否是核心线程。

如果添加成功则直接返回,如果添加失败说明有可能其他线程已经添加完毕,当前核心线程已经满了。这时会获取到当前的状态值,如果还是正常的RUNNING状态,那么就往workQueueoffer一个任务,RUNNING状态一般不会变动,除非调用shutdown等方法关闭线程池才会变动。
如果往阻塞队列workQueue中添加成功,那么再检查一次状态。

如果这时被调用关闭方法了,状态变成非RUNNING了,那么就移除该任务,同时执行拒绝策略。

否则如果当前的线程个数为0,这个就比较极端了,核心线程和非核心线程全执行完了,那么就添加一个非核心线程去执行任务,这里传入的第一个为参数null,那么添加的时候就会从阻塞队列中去取任务,因为是非核心线程,所以超时取不到任务就会自动退出。

如果核心线程满了,阻塞队列也满了,添加失败了,那么就直接通过addWorker方法添加到非核心线程中,如果添加失败则执行拒绝策略。

拒绝策略reject执行比较简单就不说了,中心就到了addWorker方法中,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);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }
		...... //省略下一段
    }

这里会发现,一上来就来了个死循环,那这里做了啥呢?
首先获取当前的运行状态,这里有一个判断语句,可以等价为这样的

 if (rs >= SHUTDOWN &&(rs != SHUTDOWN || firstTask != null 
 		|| workQueue.isEmpty()))
                return false;

那么我们就可以得出结论
SHUTDOWN 之后的状态STOP,TERMINATED等直接添加失败返回false
SHUTDOWN 状态下,如果想添加一个新的任务直接失败。或者不添加任务想创建一个线程从队列中取任务,如果此时队列为空,也直接失败。
这个条件就涵盖大部分关闭线程的场景了,可以粗略认为SHUTDOWN及之后的状态全部无法添加任务

之后内部又有一个死循环,先获取当前执行的线程数,其实就是Worker的个数,一个线程对应一个Worker,这里有两个判断,第一个是超过CAPACITY的个数,这个我们一般不会超出暂不考虑。第二是根据core进行线程数判断是否超过核心线程或者非核心线程,超出直接失败,这个对应于上面的添加核心线程失败的原因。

如果核心或非核心线程数未满,那么就通过CAS把当前的线程数+1,直接跳出循环

 private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }

如果CAS修改失败,那么就说明当前线程个数有变动,这个一般是有线程任务结束关闭退出了,这里会使用CAS把当前线程数-1,这时候就重新执行一遍内循环,重新检查一遍线程数,再重新CAS增加线程个数。
这后面还有一个判断,如果当前状态发生变化,那么就可能变更为不可用状态了,这时直接跳到开头的retry位置重来一次。

上面这么多代码实际就是给当前的ctl的线程个数的值+1,没有别的。

再看第二段代码

 private boolean addWorker(Runnable firstTask, boolean core) {
     	...... //省略第一段
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            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();
                        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;
    }

这里就有点意思了,如果上面的第一段满足,说明可以正常添加新的线程了。
这里一上来就新建了一个Worker对象,先看一下Worker的构造方法

 private final class Worker extends AbstractQueuedSynchronizer
        implements Runnable{
  Worker(Runnable firstTask) {
       setState(-1); // inhibit interrupts until runWorker
       this.firstTask = firstTask;
       this.thread = getThreadFactory().newThread(this);
  }
} 
  
 public ThreadFactory getThreadFactory() {
        return threadFactory;
    }

上面提到Worker本身就是一个Runnable,在构造方法里会通过上面的threadFactory创建一个新的线程,然后把线程的Runnable指向Worker本身,也就是这个线程start方法调用后,会直接调用Workerrun方法。

线程工厂未指定会使用默认的DefaultThreadFactory,就是普通的new出新的线程出来

 private static class DefaultThreadFactory implements ThreadFactory {
    	......
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

再回到上面的第二段代码,创建完Worker后下面的代码会使用同步加锁。

获取当前的状态,如果是<SHUTDOWN,其实就是RUNNING状态,或者是SHUTDOWN的状态同时firstTask为空,由上面第一段的代码我们指定,这时候的workQueue是不为空的,这两种情况都满足添加Worker的条件,因为可以直接添加或者从阻塞队列中取。如果当前线程已经启动了,那么就直接抛出异常,上面刚创建就被启动,这说明并发已经有先在执行了,这里直接放弃。一切正常,那么就添加到workers列表中,然后记录最大的线程个数largestPoolSize,同时把workerAdded改为true

后面的就比较简单了,添加成功后直接启动Worker的线程,其实就是使用Worker内新建的线程执行Workerrun方法,启动成功就标记线程为已启动,后面未启动成功就进入addWorkerFailed方法,addWorker方法最终返回启动成功的状态。

private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

addWorkerFailed方法只是很简单的移除任务,给当前的ctl的线程个数自旋减1,这里有个tryTerminate方法先留着后面讲。

下面看下Worker的执行代码

 private final class Worker extends AbstractQueuedSynchronizer
        implements Runnable{
	final Thread thread;
	Runnable firstTask;
    volatile long completedTasks;
        
    public void 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 (task != null || (task = getTask()) != null) {
                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);
        }
    }
}
private static boolean runStateAtLeast(int c, int s) {
     return c >= s;
}

run方法会走到内部的runWorker方法中,这里会拿到构造方法传入的Runnable任务,可能为空。如果这里拿到的任务不为空,或者用getTask从阻塞队列中取出的任务不为空,才能进行下面的代码。这里是一个While循环,也就是执行完一个后循环回来从队列中取,如果有再拿一个继续做。

  private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
         	......
		 if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    	            decrementWorkerCount();
       	         return null;
      	      }
        	......
 	 	boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

getTask中通过poll方法进行非核心线程的任务获取,超过时间会返回null,就直接跳出循环了,而核心线程默认是使用take,会一直阻塞到有任务可以取为止。注意取任务这里如果线程池状态更改,则放弃任务返回空,并把活跃线程个数减1

如果当前线程池是STOP及之后的状态,同时任务线程未被打断过,使用interrupt打断当前线程。
如果当前线程是STOP及之后的状态,如果线程被打断过,那么再打断一次。因为Thread.interrupted()会重置interupt状态值为false,后面那个一定是满足的,其实这个判断我不是很懂。
总之STOP及以后的状态,如果要运行新的任务,直接打断就完事了,注意的是,这里的打断只是说把当前线程的interupt标记改成ture,并不是说一定就把线程打死了,如果当前线程有阻塞状态wait,sleep等那么才会抛出异常,否则后续正常执行。

这里的打断前提是STOP状态,而非SHUTDOWN状态,也就是运行中的任务shutdown方法并不会打断。

这里interupt后仍然会执行task.run(),就是要我们自己处理这个标记,beforeExecuteafterExecute都是空方法,可以自定义实现。这个run中可能会有异常抛出,那么后面的completedAbruptly就不会赋值为false,同时会把异常个数统计数减1。

任务执行完后(打断也算),会把completedTasks++然后执行processWorkerExit方法,表示Work正常退出。这里传入的completedAbruptly一般是false

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
            }
            addWorker(null, false);
        }
    }

private static boolean runStateLessThan(int c, int s) {
     return c < s;
}

这里会先移除执行完毕的Worker,然后把Worker内完成数completedTasks累加到全局的计数上。
如果是RUNNING或者SHUTDOWN的状态,会进行下面的判断。为什么要允许SHUTDOWN的状态,因为addWorker方法中允许SHUTDOWN状态时添加一个非核心线程的空任务Worker去执行一定处理,这也就从侧面说明shutdown方法调用后,Worker虽然不支持添加新的任务,但仍然可以从原来的队列中取任务执行
1.如果允许核心线程超时,当前队列不为空同时当前的线程个数大于1,或者队列为空,正常返回
2.如果不允许核心线程超时,同时当前线程个数大于核心线程数,正常返回。

而最后的addWorker什么时候会进入执行呢?其实就是completedAbruptly = true或者有线程异常结束。这两种情况一般都是因为
1.执行的任务Runnable抛出异常导致completedAbruptly并没有赋值为false
2.核心线程异常关闭,对应于上面的第2条,非核心不允许超时,但同时有线程关闭,导致核心线程小于corePoolSize,那么就是核心线程的关闭

发生异常那么就通过addWorker新建一个非核心线程去补齐线程数。

这里有个tryTerminate也比较重要

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();
            }
        }
    }

如果是运行态RUNNING,或者SHUTDOWN态同时队列任务不为空,或者TIDYING及之后的状态直接返回,TIDYING是在这个方法的下面进行自旋设置,刚开始进来肯定是不满足这个的。

其实首次满足只有SHUTDOWN态同时任务队列为空,或者STOP态度,才会走到下面的代码。

如果活跃线程数大于0,那么通过interruptIdleWorkers尝试打断空闲的线程。如果活跃线程已经变成0了,就是核心和非核心线程都关闭了,那么先自旋设置TIDYING,最终设置成TERMINATED,并唤醒所有termination等待的线程。

有人可能会问,既然到这里线程池中线程已经全部关闭了,那为什么还要唤醒等待的线程?其实线程池中有termination锁定等待的只有awaitTermination方法,线程池内部是不会调用的,也就是给我们自己使用的。

public boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            while (!runStateAtLeast(ctl.get(), TERMINATED)) {
                if (nanos <= 0L)
                    return false;
                nanos = termination.awaitNanos(nanos);
            }
            return true;
        } finally {
            mainLock.unlock();
        }
    }

这个方法一般是用来检测线程池是否关闭了的,有超时时间设置,超过时间没被唤醒termination.awaitNanos就会返回0或负数,也就是这个方法的返回值就是线程池是否已经关闭。通常配合shutdown方法使用,比如:

  ExecutorService executorService = Executors.newFixedThreadPool(5);
  executorService.execute(testRunner); //耗时任务
  executorService.shutdown();
  System.out.println("任务开始执行 ");
  boolean result = executorService.awaitTermination(1000, TimeUnit.MILLISECONDS);
  System.out.println("当前线程池是否关闭  "+result);

这个结果可能是关闭也可能还未关闭,比较shutdown并不会打断正在执行的线程。
看下面的shutdown方法你就会明白为什么不会打断运行中的线程任务了

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

这里的checkShutdownAccessonShutdown最终都是空方法,跳过。
advanceRunState方法是自旋把当前线程池状态设置为SHUTDOWN状态,重点看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这个锁,因为Worker本身就继承了AQS,所以可以当成一把锁来用的。tryLock方法比较温和,尝试获得锁,获得不到就返回false,只有拿到这把锁才会执行interupt,那么这把锁是在哪锁定的呢?

其实就在上面的runWorker方法内,也就是任务正常执行期间是拿不到这个锁的,也就无法打断了,除非任务结束或抛出异常。

	//省略部分代码
    final void runWorker(Worker w) {
     	......
        w.unlock(); // allow interrupts
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                try {
                      task.run();
                } finally {
                     w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

shutdown对应的就是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;
    }
    
 private List<Runnable> drainQueue() {
        BlockingQueue<Runnable> q = workQueue;
        ArrayList<Runnable> taskList = new ArrayList<>();
        q.drainTo(taskList);
        if (!q.isEmpty()) {
            for (Runnable r : q.toArray(new Runnable[0])) {
                if (q.remove(r))
                    taskList.add(r);
            }
        }
        return taskList;
    }

shutdown类似,会设置当前状态为STOP,并调用interruptWorkers方法,最后使用drainQueue方法弹出所有阻塞队列中的任务,并全部从队列中移除,但是会把这些返给调用者,shutdownNow方法是可以获得未执行的阻塞任务集合的。此外shutdownshutdownNow最终都会调用上面提到的tryTerminate方法。

    private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
    }
    
	void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }

shutdown就比较暴力了,不管三七二十一,只要当前任务线程不为空而且未被打断过,就直接打断。打断除了可打断正在执行的耗时Runnable,对于处于获取任务状态的阻塞状态也是可以适用的,再回头看一下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;
            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;
            }
        }
    }

打断的是最后面的workQueue.poll或者workQueue.take()方法,抛出异常后重新进入循环,那么此时是STOP及以上状态,或者SHUTDOWN状态,同时队列中没有任务了,就返回null,那么最外面的runWorker方法也就会终止退出了。所以说SHUTDOWN比较温和,只有队列中没有剩余任务才会退出,并不是说本次正在执行的任务执行完就退出了,STOP就很暴力,通篇都展示这STOP的不讲道理。

3.总结

1. 线程池通过添加Worker的方式创建新的线程,其中Worker中会使用ThreadFactory构建新的线程,并保存传入的首次要执行任务,可能为空。可以说一个Worker对应一个线程。
2. Worker中会运行初始化传入的Runnable,如果为空,那么就从阻塞队列workQueue中通过getTask获取任务,如果当前没有任务,那么就阻塞。
3. 非核心线程的getTask方法是有阻塞超时时间的,超出时间获取不到就最终返回null,那么Worker的执行任务的方法runWorker也会终止,当前的Worker结束,从线程池中移除。如果是核心线程,那么如果不设置允许核心线程超时,核心线程会一直阻塞到有任务可以取为止,不会终止。
4. 如果执行的Worker任务抛出异常,同时当前线程池没有调用过shutdownNow方法,也就是当前是STOP前面的几种状态,那么会给线程池重新创建一个非核心线程进行补全。
5. shutdown 方法并不打断正在执行任务的Worker,因为Worker执行任务前会把自身作为锁进行锁定,执行完一个任务(Runnable)后,会进行解锁。shutdown方法会尝试获得Worker本身这把锁,获取不到就不会打断了。那么对于一个正在运行的Worker,当执行完当前的任务,循环再去取队列中的任务,发现没有新任务然后阻塞后,这时候是可以被shutdown打断的。
6. shutdown方法执行后,线程池不再接受新的任务,原先队列中的任务保留。
7. shutdownNow方法执行后,线程池同样不再接受新的任务,原先队列中的任务会被全部弹出移除,同时这些任务会放在一个集合中,作为shutdownNow的返回值。同时getTask方法返回值全部为空(只要变成了STOP状态就不给任务了)。
8. shutdownNow方法会强制执行所有Worker的线程打断方法interupt,而不像shutdown那种先尝试获取锁。
9. awaitTermination可以配合shutdown等方法,判断线程池是否已经完全关闭了,即没有线程在执行任务了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值