线程池原理(三):ThreadPoolExecutor
来源:互联网 发布:mysql 大于等于 编辑:程序博客网 时间:2024/06/05 01:09
ThreadPoolExecutor就是我们经常说的大名鼎鼎的线程池,Executors工厂创建的线程池都是该类的实例,通过调节参数的大小创建适用于各个场景的线程池。
接下来详细分析下ThreadPoolExecutor的源码,先看下定义:
public class ThreadPoolExecutor extends AbstractExecutorService {}
ThreadPoolExecutor继承了AbstractExecutorService,该抽象类为线程池提供了默认实现。后面讲到线程池代码时详细说明。
先通过构造函数来初步了解线程池
构造函数
ThreadPoolExecutor有很多重载的构造函数,所有构造函数最终都调用了一个构造函数,只是有些构造函数有默认参数而已,看下最终调用的构造函数:
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;}
这个构造函数有很多参数,我们分别来解释每一个参数的意义
- corePoolSize:核心线程数,当提交一个新的任务到线程池,如果当前线程池运行的线程数(包括闲置的线程)小于核心线程数,则会创建一个新的线程作为核心线程来执行该任务。
- maximumPoolSize:线程池允许最大的线程数,当提交一个新的任务到线程池,如果当前线程池运行的线程数(包括闲置的线程)大于corePoolSize,小于maximumPoolSize,并且等待队列满的时候,会创建一个新的线程来处理该任务。
- keepAliveTime:当线程池中线程数量大于corePoolSize时,闲置线程最长可以存活的时间。
- unit:时间单位。
- workQueue:保存任务的队列,当池中线程数大于corePoolSize时,新来的任务保存到该队列。
- threadFactory:线程工厂,线程池中的线程都是通过这个工厂创建的。
- handler:任务拒绝执行策略,当线程池无法处理新来任务时的处理策略。
讲到这,有必要讲下ThreadPoolExecutor的设计思路,了解了设计思路对后面源码的分析会有更好的效果。
图1 线程池示意图
通过图1我们介绍下线程池的设计思路:
- 当一个任务通过submit或者execute方法提交到线程池的时候,如果当前池中线程数(包括闲置线程)小于coolPoolSize,则创建一个线程执行该任务。
- 如果当前池中线程数大于等于coolPoolSize,则将该任务加入到等待队列。
- 如果任务不能入队,说明等待队列已满,若当前池中线程数小于maximumPoolSize,则创建一个临时线程(非核心线程)执行该任务。
- 如果当前池中线程数已经等于maximumPoolSize,此时无法执行该任务,根据拒绝执行策略处理,后面还会详细讲解具体的拒绝执行策略。
以上4步是线程池处理到达任务的主要流程。当池中线程数大于coolPoolSize,超过keepAlive时间的闲置线程会被回收掉。注意,回收的是非核心线程,核心线程一般是不会回收的。如果设置allowCoreThreadTimeOut(true),则核心线程在闲置keepAlive时间后也会被回收。
任务队列是一个阻塞队列,线程执行完任务后会去队列取任务来执行,如果队列为空,线程就会阻塞,直到取到任务。
接下来继续学习源码:
submit方法
通常情况下我们通过submit方法向线程池提交并执行任务,线程池的submit方法都是在子类AbstractExecutorService实现的,并且有多个重载的方法,看下这些方法的实现:
//提交Runnable的任务,通过newTaskFor方法包装成RunnableFuturepublic Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); //指定返回值为null RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask;}//提交Runnable的任务,指定返回值为resultpublic <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask;}//提交Callable的任务public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask;}
这几个重载方法首先通过newTaskFor方法将任务包装成一个RunnableFuture,然后调用execute方法执行任务。
先看下newTaskFor方法:
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value);}protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable);}
这两个重载方法很简单,创建一个FutureTask对象返回,之后我们就可以通过这个对象的get方法获取任务执行结果了。
AbstractExecutorService这几个重载的submit方法最终都调用了execute方法,通过该方法真正执行任务。
execute方法
execute是真正执行任务的方法,分析execute源码之前先来看下ThreadPoolExecutor的状态字段定义:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); //COUNT_BITS计算后等于29,活动线程数占用的位数 private static final int COUNT_BITS = Integer.SIZE - 3; //活动线程最大数量 private static final int CAPACITY = (1 << COUNT_BITS) - 1; //线程池5种运行状态,保存在ctl高3位 //11111111 11111111 11111111 11111111左移29位后只保留高位3个1即: //11100000 00000000 00000000 00000000 private static final int RUNNING = -1 << COUNT_BITS; //0左移29位后 //00000000 00000000 00000000 00000000 private static final int SHUTDOWN = 0 << COUNT_BITS; //1左移29位后 //00100000 00000000 00000000 00000000 private static final int STOP = 1 << COUNT_BITS; //2左移29位后 //01000000 00000000 00000000 00000000 private static final int TIDYING = 2 << COUNT_BITS; //3左移29位后 //01100000 00000000 00000000 00000000 private static final int TERMINATED = 3 << COUNT_BITS;
线程池维护了一个int原子变量ctl,表示线程池当前状态。通过这一个字段表示线程池当前活动线程数和线程池的运行状态。其中低29位用来表示活动线程数,高3位用来表示线程池的运行状态。
线程池的运行状态有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。状态所对应的数字其实没有什么意义,重点需要了解状态代表的含义。
- RUNNING:该状态下的线程池可以接受新任务,并且可以处理等待队列中的任务。
- SHUTDOWN:该状态下的线程池不再接受新任务,但是可以处理等待队列中的任务。
- STOP:该状态下的线程池不再接受新任务,不再处理等待队列中的任务,会中断正在执行的任务。
- TIDYING:所有的任务都已经中止,活动线程数为0,此状态下的线程池即将转移到TERMINATED状态。
- TERMINATED:terminated()执行完后到达此状态。
线程池的状态转移包括如下几个:
- RUNNING -> SHUTDOWN,在执行shutdown()方法时,线程池经历了这种状态转移过程。
- RUNNING -> STOP或者SHUTDOWN -> STOP,在执行shutdownNow()方法时,线程池经历了这种状态转移过程。
- SHUTDOWN -> TIDYING,当等待队列和池中的任务都为空时,经历了这种状态转移过程。
- STOP -> TIDYING,池中任务为空时,经历这种状态转移过程。
- TIDYING -> TERMINATED,执行terminated()方法时经历这个状态转移过程。
接下来看execute的源码:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); //取线程池当前状态 int c = ctl.get(); //线程数小于核心线程数,创建一个核心线程,并将任务作为该线程第一个任务 //如果创建线程失败,返回false if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; //重新获取状态 c = ctl.get(); } //尝试将任务添加到等待队列 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); //重新判断线程池是否处于RUNNING状态,若不处于RUNNING状态,删除等待队列中该任务并拒绝任务 if (! isRunning(recheck) && remove(command)) reject(command); //如果没有线程则创建一个非核心线程 else if (workerCountOf(recheck) == 0) addWorker(null, false); } //任务添加到等待队列失败,尝试创建一个非核心线程执行该任务,创建失败则拒绝执行任务 else if (!addWorker(command, false)) reject(command);}//当前活动线程数量private static int workerCountOf(int c) { //c & 00011111 11111111 11111111 11111111 //"与"运算取低29位的值 return c & CAPACITY; }
execute的执行逻辑其实前面已经提到了,这里根据代码再分析下:
- 对于空的任务,线程池会抛出NPE异常
- 通过workerCountOf方法获取线程池的线程数,若线程数小于核心线程数,创建一个核心线程并将任务作为该核心线程的第一个任务。若创建线程失败,重新获取线程池状态。
- 尝试将任务添加到等待队列,需要注意的是,任务添加到等待队列成功后,需要进一步检查线程池状态,因为这个过程线程池的状态可能已经改变。
- 尝试将任务添加到等待队列,添加失败拒绝执行任务。
workCountOf方法很简单,通过”与”运算取ctl的低29位的值。
看下addWorker方法,该方法是线程池的重点:
addWorker方法
//firstTask:当池中线程数小于corePoolSize或者等待队列已满,创建的工作者线程执行的第一个任务//core:是否作为核心线程private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); //线程池当前状态 int rs = runStateOf(c); //这个条件看起来有点晕,不着急,我们仔细分析下 //原判断条件为: //rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()) //如果线程池状态为RUNNING,或者 //线程池状态是SHUTDOWN,并且firstTask为空,并且等待队列不为空,可以接受任务。 //其他情况下,addWorker直接返回false。 //通俗点讲,就是线程池处于SHUTDOWN状态时,还可以处理等待队列中的任务,但是不可以接受新任务了。 //RUNNING状态下的线程池当然可以接受新的任务了 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); //线程池中线程数量是否达到上限 //核心线程数的上限是coolPoolSize,非核心线程数的上限是maximumPoolSize if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; //增加线程数成功,结束retry对应的for循环 if (compareAndIncrementWorkerCount(c)) break retry; //重新读取状态值 c = ctl.get(); //状态改变了,到retry处重新开始for循环 if (runStateOf(c) != rs) continue retry; } } //到这里说明CAS增加线程数成功了 boolean workerStarted = false; boolean workerAdded = false; //Worker是线程池实现的内部类,实现了AQS和Runnable,包装了需要执行的任务和执行的线程 //Worker就是线程池的工作线程,是干活的工人 Worker w = null; try { final ReentrantLock mainLock = this.mainLock; //创建一个工作者线程 w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { mainLock.lock(); try { //获取锁之后重新获取状态 int c = ctl.get(); int rs = runStateOf(c); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { //如果该线程已经启动了,抛出异常,因为我们稍后才会启动该线程 if (t.isAlive()) throw new IllegalThreadStateException(); //workers是线程池的私有属性,存储了Worker workers.add(w); int s = workers.size(); //更新线程池的最大数量 if (s > largestPoolSize) largestPoolSize = s; //添加成功了 workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { //如果添加成功了,启动线程 t.start(); //启动成功了 workerStarted = true; } } } finally { //处理启动失败的情况,回滚,从workers中移除该worker,将wc减1 if (! workerStarted) addWorkerFailed(w); } //返回添加的线程是否启动成功 return workerStarted;}
注释已经很详细了,这里再说下该方法的思路:
- 首先试图原子地增加线程数,这个过程需要检查ctl的状态,如果检查发现不能创建新worker,返回false。否则自旋CAS增加线程数,直到设置成功。
- 线程数增加成功后,真正创建worker并添加到workers工作集合中。创建worker成功后,启动该工作者线程,返回是否启动成功。如果启动worker失败,需要做回滚操作,从workers中移除该worker,并将wc减1。
工作线程-worker
Worker是线程池的内部类,它封装了Thread和Runnable。看下该内部类的实现:
//Worker实现了AQS,提供了锁操作private final class Worker extends AbstractQueuedSynchronizer implements Runnable { private static final long serialVersionUID = 6138294804551838833L; //运行任务的线程 final Thread thread; //执行的第一个任务,第一个任务可能为空 Runnable firstTask; //该工作者已经执行完成的任务 volatile long completedTasks; Worker(Runnable firstTask) { //设置锁的状态为-1 setState(-1); this.firstTask = firstTask; //通过线程工厂新建一个线程,要执行的任务就是本Worker //前面讲到addWorker方法,线程创建成功后会启动线程,线程执行的的任务正是本Worker,也就是 //执行run方法 this.thread = getThreadFactory().newThread(this); } public void run() { //addWorker方法启动的线程最终会执行runWorker方法,该方法线程会从队列中取出任务执行 //若队列中没有任务可以执行,线程会阻塞 runWorker(this); } //下面几个方法都是锁操作,这里不再介绍 protected boolean isHeldExclusively() { return getState() != 0; } //……}
前面讲到的addWorker方法,该方法创建了Worker实例并将firstTask作为Worker构造函数的参数。firstTask作为Worker第一个运行的任务。Worker构造函数创建线程的时候将firstTask作为该线程的Runnable参数。启动该线程的时候执行本Worker的run方法。run方法会调用runWorker,当线程执行完它的firstTask后会从等待队列取任务来执行,若等待队列为空,该线程就会阻塞等待,直到等待队列不空。好了,我们重点看下runWorker方法,该方法是线程真正执行任务的地方,看下runWorker方法源码:
runWorker方法
//该方法最终被工作者线程执行final void runWorker(Worker w) { Thread wt = Thread.currentThread(); //worker的第一个任务 Runnable task = w.firstTask; w.firstTask = null; w.unlock(); boolean completedAbruptly = true; try { //task为空的时候去等待队列获取,可能会被阻塞 while (task != null || (task = getTask()) != null) { //这里说明了为何Worker实现AQS,将它作为一个锁使用,执行任务之前先锁住 w.lock(); //1. 若线程池的状态处于STOP、TYDYING或者TERMINATED,当然不能再执行任务了,确保要中断当前线程。 //2. 如果线程池的状态处于RUNNING或者SHUTDOWN,需要确保当前线程不在中断状态。怎么确保?那就只有 //清除线程的中断状态了,清除完线程状态后需要重新检查线程池状态是否为STOP、TYDYING或者 //TERMINZTED,因为清除线程中断状态期间线程池状态可能会被改变,如果是,还得继续将线程中断。 //嗯,的确很复杂,很绕!! 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(); /* 1. 通过线程池submit方法提交的任务都会被包装成FutureTask,也就是这里的run方法就是 FutureTask的run方法,而该方法捕获了异常并保存异常信息,因此这里的异常不会继续抛出,下面几 个捕获异常的代码块就捕获不到异常了。调用Future的get方法会获得run方法中抛出的异常信息。 2. 通过线程池的execute方法提交的Runnable任务,如果抛出异常在这里将会被捕获。 */ } 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; //worker完成的任务数量递增 w.completedTasks++; //释放锁 w.unlock(); } } //到这里说明该worker正常退出了,不再从等待队列中取任务执行 //completedAbruptly=true说明是由于异常退出了 completedAbruptly = false; } finally { //worker退出后执行清理工作 processWorkerExit(w, completedAbruptly); }}
runWorker方法的执行逻辑:
- 首先执行worker的firstTask。
- 从等待队列取任务执行,若取不到则阻塞等待,具体根据特定规则,后面讲到getTask方法时再介绍。
继续看getTask源码:
getTask方法
//返回null表示当前worker必须退出private Runnable getTask() { //上次取任务是否超时 boolean timedOut = false; retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); //STOP以及以上的状态就不再处理等待队列中的任务。 //SHUTDOWN状态可以继续处理等待队列中的任务,当然如果等待队列为空时就返回null了。 if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } boolean timed; for (;;) { int wc = workerCountOf(c); timed = allowCoreThreadTimeOut || wc > corePoolSize; //若wc大于maximumPoolSize,需要将当前worker退出,返回null。 //若wc不大于maximumPoolSize,且上次获取任务超时,且允许核心线程超时退出或者wc大于 //corePoolsize,需要将当前worker退出,返回null。 if (wc <= maximumPoolSize && ! (timedOut && timed)) break; //worker数量已经大于maximumPoolSize了,将ctl的workerCount减1后返回null if (compareAndDecrementWorkerCount(c)) return null; c = ctl.get(); if (runStateOf(c) != rs) continue retry; } try { //从阻塞队列中取任务,阻塞队列作为一个重要专题以后再花时间介绍 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); //从等待队列中取到了任务 if (r != null) return r; //取任务超时了,下次可能需要将该worker退出 timedOut = true; } catch (InterruptedException retry) { timedOut = false; } }}
getTask的返回值表示当前worker是否需要退出,总结下该方法的逻辑:
- 如果当前已经有超过maximumPoolSize的线程,将当前线程数减1并返回null。
- 如果线程池已经处于STOP及以上的状态,将当前线程数减1并返回null。
- 如果线程池处于SHUTDOWN状态并且等待队列为空,将当前线程数减1并返回null。
- 如果当前worker等待获取任务超时,并且允许核心线程超时退出或者当前线程数大于corePoolSize,将当前线程数减1并返回null。
runWorker执行完成后调用processWorkerExit方法执行清理工作。看下该清理方法:
processWorkerExit方法
//工作者线程退出后执行清理工作//w:退出的worker//completedAbruptly:worker是否异常退出private void processWorkerExit(Worker w, boolean completedAbruptly) { //如果异常退出,将ctl的workerCount减1 //因为异常退出时workerCount不会被修正 if (completedAbruptly) decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //仅在worker退出后统计已经完成的任务数量 completedTaskCount += w.completedTasks; //从workers集合中移除w workers.remove(w); } finally { mainLock.unlock(); } //尝试终止线程池,稍后详细介绍 tryTerminate(); int c = ctl.get(); if (runStateLessThan(c, STOP)) { //因为已经移除了一个worker,这里判断是否需要给线程池添加一个worker以代替刚才那个移除的worker if (!completedAbruptly) { //这里worker是正常退出的,非正常退出的肯定需要增加worker /* 1. 不允许核心线程超时退出。若当前线程池的workerCount>=corePoolSize,不需要再添加worker。 否则就给线程池增加一个worker。 2. 允许核心线程超时退出。若等待队列不空,但是没有工作者线程,则需要添加一个worker,否则不需要增 加worker。 */ int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; //如果当前有足够多的工作线程,则不需要增加worker if (workerCountOf(c) >= min) return; } //给线程池补血,增加worker作为非核心线程 addWorker(null, false); }}
processWorkerExit根据worker是否异常退出决定后续处理。
- 如果是异常退出则将ctl中的workerCount减少1,如果是正常退出的,ctl的workerCount肯定已经修正了,不需要再处理。
- 不管worker是否异常退出,worker退出后都需要记录当前线程池已经完成的任务数量,记录完成后将该worker从线程池的workers集合中移除。
- 尝试终止线程池。
- 根据线程池的配制,决定是否增加worker以代替刚才被移除的worker,详见代码注释。
继续看tryTerminate方法:
tryTerminate方法
//尝试终止线程池//该方法可能会被shutdown、shutdownNow等方法调用final void tryTerminate() { for (;;) { int c = ctl.get(); /* 处于以下几个状态不能终止线程池,直接返回 1. 线程池还在运行状态,不能终止线程池。 2. 线程池的状态已经是TIDYING或者TERMINATED状态了,说明线程已经处于或即将处于终止状态。 3. 线程池的状态是SHUTDOWN,但是等待队列不为空,说明有任务还要执行。 */ if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) return; /* 如果活动线程数大于0,中断一个空闲的worker。 interruptIdleWorkers中会遍历workers,直到成功中断了一个worker。遍历过程中会判断worker 是否被锁住,如果锁住说明该worker目前正忙,不可以中断,否则说明该worker处于idle状态,尝试中断。 */ if (workerCountOf(c) != 0) { interruptIdleWorkers(ONLY_ONE); //活动线程数大于0,不能终止线程池,直接返回 return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //尝试将ctl设置为TIDYING状态 if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { try { //空方法,留给子类实现 terminated(); } finally { //ctl最终状态置为TERMINATED,线程数置为0 ctl.set(ctlOf(TERMINATED, 0)); //已经终止线程了,唤醒在termination上等待的线程 //线程池的awaitTermination方法会在该条件等待 termination.signalAll(); } return; } } finally { mainLock.unlock(); } }}
RejectedExecutionHandler
execute不能执行任务时调用reject方法处理。reject的处理逻辑是调用拒绝执行处理器的rejectedExecution方法:
final void reject(Runnable command) { handler.rejectedExecution(command, this);}
可以通过线程池的构造函数或者setRejectedExecutionHandler方法设置拒绝执行处理器,如果不设置,会有默认的处理器:AbortPolicy。
下面详细介绍线程池的拒绝执行策略,拒绝执行处理器是一个接口,即:RejectedExecutionHandler,该接口仅有一个方法rejectedExecution,看下该接口的源码:
public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor);}
线程池实现了四种拒绝执行策略,且都是通过ThreadPoolExecutor的静态内部类实现,这四种拒绝策略分别是:
AbortPolicy:这也是线程池的默认拒绝执行策略。该策略仅会抛出RejectedExecutionException异常,是最简单的策略。
DiscardPolicy:这个策略的实现其实什么也没做,什么也没有做意味着简单的丢弃任务,也是一个简单的拒绝策略。
CallerRunsPolicy:调用方执行策略,既然线程池中没有线程可以执行该任务,那就让运行线程池的线程来执行该任务。看下该类的实现:
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { //调用r的run方法,这是由运行线程池的线程来运行 r.run(); } }}
DiscardOldestPolicy:丢弃等待队列中最老的任务,并执行新的任务。看下该类的实现:
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { //丢弃队列中最老的任务后立即执行任务r e.getQueue().poll(); e.execute(r); } }}
shutdown方法
shutdown方法会关闭线程池,不再接受新的任务,但是等待队列中的任务还是会执行的。
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //安全管理器检查调用方是否有权限关闭线程池,是否有权限中断线程池中每个线程 checkShutdownAccess(); //将线程池的runState更改为SHUTDOWN,如果已经是SHUTDOWN,什么也不做 advanceRunState(SHUTDOWN); //中断所有空闲的worker interruptIdleWorkers(); //空的方法 onShutdown(); } finally { mainLock.unlock(); } //尝试终止线程池 tryTerminate();}
shutdownNow方法
shutdownNow方法关闭线程池,停止所有正在执行的任务,移除等待队列中的任务。
//返回正在等待执行的任务,这些任务是从等待队列移除的public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); advanceRunState(STOP); //中断所有线程 interruptWorkers(); //将等待队列中的任务移到tasks中 tasks = drainQueue(); } finally { mainLock.unlock(); } tryTerminate(); return tasks;}
allowCoreThreadTimeout方法
该方法允许核心线程超时退出,默认情况下,核心线程超时是不会退出的。
///是否允许核心线程超时public void allowCoreThreadTimeOut(boolean value) { //允许核心线程超时,keepAliveTime必须大于0 if (value && keepAliveTime <= 0) throw new IllegalArgumentException("Core threads must have nonzero keep alive times"); //当前线程池的配制和value不一样时才去设置 if (value != allowCoreThreadTimeOut) { allowCoreThreadTimeOut = value; //中断空闲的线程,包括核心线程 if (value) interruptIdleWorkers(); }}
awaitTermination方法
等待线程池终止,参数timeout是最多等待的时间。若timeout时间后还未终止,返回false。
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //自旋等待线程池终止 for (;;) { //线程池已经终止,返回true if (runStateAtLeast(ctl.get(), TERMINATED)) return true; //已经超时,返回false if (nanos <= 0) return false; //在条件termination上等待 nanos = termination.awaitNanos(nanos); } } finally { mainLock.unlock(); }}
getActiveAccount方法
该方法返回正在执行任务的线程,通过worker是否上锁来判断。
public int getActiveCount() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { int n = 0; //已经锁住的worker,正在执行任务 for (Worker w : workers) if (w.isLocked()) ++n; return n; } finally { mainLock.unlock(); }}
prestartAllCoreThreads方法
通常情况下,当任务提交到线程池,没有worker才会去创建worker。我们还可以在线程池接受任务之前预启动所有核心线程,这样当任务到达的时候,直接就可以提供服务了。
public int prestartAllCoreThreads() { int n = 0; //创建worker作为核心线程,因为是预启动,所以第一个任务都是null //返回false说明已经添加完成了corePoolSize个核心线程 while (addWorker(null, true)) ++n; return n;}
getCompletedTaskCount方法
该方法返回线程池已经执行完成的任务数量,通过遍历每个worker已经完成的任务来完成的。
public long getCompletedTaskCount() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //completedTaskCount仅仅在worker退出时更新,下面遍历的workers并不包含这些退出的worker long n = completedTaskCount; for (Worker w : workers) n += w.completedTasks; return n; } finally { mainLock.unlock(); }}
invokeAll方法
该方法是子类AbstractExecutorService的方法,批量提交并执行任务,等待所有任务执行完成该方法才会返回。
//批量提交并执行任务public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { if (tasks == null) throw new NullPointerException(); //每一个任务的执行都有一个Future返回值 ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size()); boolean done = false; try { for (Callable<T> t : tasks) { RunnableFuture<T> f = newTaskFor(t); //保存任务返回值 futures.add(f); execute(f); } for (int i = 0, size = futures.size(); i < size; i++) { Future<T> f = futures.get(i); if (!f.isDone()) { try { //等待该任务执行完成才返回 f.get(); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } } } done = true; //返回任务执行结果 return futures; } finally { //如果任务执行出错(出现异常),取消这些任务 if (!done) for (int i = 0, size = futures.size(); i < size; i++) futures.get(i).cancel(true); }}
- 线程池原理(三):ThreadPoolExecutor
- 线程池ThreadPoolExecutor的原理
- 并发编程--线程池ThreadPoolExecutor实现原理(二)
- JAVA线程池(ThreadPoolExecutor)原理分析与使用
- java线程池学习(三) —— ThreadPoolExecutor
- java线程池ThreadPoolExecutor原理及使用
- Java线程池ThreadPoolExecutor的execute()原理
- Java中线程池ThreadPoolExecutor原理探究
- java 线程池(ThreadPoolExecutor)
- java 线程池(ThreadPoolExecutor)
- 线程学习三:线程池ThreadPoolExecutor 与 Executors
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java中的线程池——ThreadPoolExecutor的原理
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java ThreadPoolExecutor线程池原理及源码分析
- 一道递归问题
- [BZOJ]1115 石子游戏Kam
- hdu 6178 dfs+超神读入挂
- java求1000以内的水仙花数
- LightOJ 1042 Secret Origins(贪心)
- 线程池原理(三):ThreadPoolExecutor
- Java的for循环上界可变性探讨
- java类如何按照某一属性排序
- oracle建表关联外键时报invalid datatype错误
- Android Studio 添加mysql后提示版本不对
- LA7984—Delivering Goods(最短路+DAG上的最少路径覆盖)
- py2topy3+cmd 命令
- 判断素数
- 基于朴素贝叶斯分类器的文本分类