赞
踩
首先区分概念,任务和线程。可以简单理解为任务为Runnable,线程为Thread。ThreadPoolExecutor内部维持的是线程池,因为创建线程比较耗时耗资源。而内部维护任务使用的是BlockingQueue。
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 final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
- private static final int COUNT_BITS = Integer.SIZE - 3;
- // 低29位表示线程数量
- private static final int CAPACITY = (1 << COUNT_BITS) - 1; //即二进制11111111111111111111111111111 (29位)
-
- // runState is stored in the high-order bits 线程池运行状态存储在32位的高3位
- private static final int RUNNING = -1 << COUNT_BITS; // 即二进制11100000000000000000000000000000(32位)
- private static final int SHUTDOWN = 0 << COUNT_BITS; // 即二进制00000000000000000000000000000000(32位)
- private static final int STOP = 1 << COUNT_BITS; // 即二进制00100000000000000000000000000000(32位)
- private static final int TIDYING = 2 << COUNT_BITS; // 即二进制01000000000000000000000000000000(32位)
- private static final int TERMINATED = 3 << COUNT_BITS; // 即二进制01100000000000000000000000000000(32位)
下面介绍各个线程池各个状态的含义:
RUNNING
接受新任务并且处理已经进入队列的任务
SHUTDOWN
不接受新任务,但是处理已经进入队列的任务
STOP
不接受新任务,不处理已经进入队列的任务,并且中断正在执行的任务
TIDYING
所有任务执行完成,workerCount为0。线程转到了状态TIDYING会执行terminated()钩子方法
TERMINATED
terminated()已经执行完成
状态之间可以相互转换
RUNNING -> SHUTDOWN
调用了shutdown()方法
(RUNNING 或 SHUTDOWN) -> STOP
调用了shutdownNow()
SHUTDOWN -> TIDYING
当队列和线程池为空
STOP -> TIDYING
当线程池为空
TIDYING -> TERMINATED
当terminated()钩子方法执行完成
- 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的初始值是:ctlOf(RUNNING, 0)即11100000000000000000000000000000,如果新建一个线程,ctl就增加1,等于11100000000000000000000000000001,workerCountOf计算工作线程数量,即11100000000000000000000000000001 &11111111111111111111111111111 = 1,runStateOf计算状态值,~CAPACITY(取反) = 11100000000000000000000000000000;即11100000000000000000000000000001 & 11100000000000000000000000000000 = 11100000000000000000000000000000,等于RUNNING 状态。JDK源码中有不少地方巧妙的运用了位运算,熟悉很有必要。
ThreadPoolExecutor有几个构造方法,构造方法中有几个参数,分别是corePoolSize、maximunPoolSize、keepAliveTime、unit、workQueue、threadFactory和handler。下面分别介绍这个几个参数:
corePoolSize
核心线程的数量。默认是没有超时的,也就是说就算线程闲置,也不会被处理。但是如果设置了allowCoreTimeOut为true,那么当核心线程闲置时,会被回收。
maximumPoolSize
最大线程池尺寸,被CAPACITY限制(2^29-1)。
keepAliveTime
闲置线程被回收的时间限制
unit
keepAliveTime的单位
workQueue
用于存放任务的队列
threadFactory
创建线程的工厂类
handler
当任务执行失败时,使用handler通知调用者
当创建好一个ThreadPoolExecutor对象后,调用execute(Runnable r)方法执行任务。下面是execute方法的实现:
- public void execute(Runnable command) {
- if (command == null)
- throw new NullPointerException();
- /*
- * Proceed in 3 steps:
- *
- * 1. If fewer than corePoolSize threads are running, try to
- * start a new thread with the given command as its first
- * task. The call to addWorker atomically checks runState and
- * workerCount, and so prevents false alarms that would add
- * threads when it shouldn't, by returning false.
- *
- * 2. If a task can be successfully queued, then we still need
- * to double-check whether we should have added a thread
- * (because existing ones died since last checking) or that
- * the pool shut down since entry into this method. So we
- * recheck state and if necessary roll back the enqueuing if
- * stopped, or start a new thread if there are none.
- *
- * 3. If we cannot queue task, then we try to add a new
- * thread. If it fails, we know we are shut down or saturated
- * and so reject the task.
- */
- int c = ctl.get();
- // 1.
- if (workerCountOf(c) < corePoolSize) {
- if (addWorker(command, true))
- return;
- c = ctl.get();
- }
- // 2.
- if (isRunning(c) && workQueue.offer(command)) {
- int recheck = ctl.get();
- if (! isRunning(recheck) && remove(command))
- reject(command);
- else if (workerCountOf(recheck) == 0)
- /* 线程池处于Running状态,创建一个非核心线程,没有把任务command交给它呢是因为任务已经
- 进入队列了,null表示新建线程,但不会启动它,false表示最大线程树与非核心线程数为界限*/
- addWorker(null, false);
- }
- // 3.
- else if (!addWorker(command, false))
- reject(command);
- }
一个任务command进入线程池的执行流程,方法execute(command)分3步进行:
1,判断当前线程数是否大于核心线程数,小于则通过addWorker方法创建一个新线程(核心线程)执行任务;
2,如果当前线程数大于核心线程数,判断线程池状态,处于RUNNING,则将任务添加到阻塞队列;再次检查线程池状态,
3,如果任务command添加到阻塞队列失败,直接创建一个非核心线程执行它,失败则执行拒绝策略。
为什么需要double check线程池的状态?
在多线程环境下,线程池的状态时刻在变化,而ctl.get()是非原子操作,很有可能刚获取了线程池状态后线程池状态就改变了。判断是否将command加入workque是线程池之前的状态。倘若没有double check,万一线程池处于非running状态(在多线程环境下很有可能发生),那么command永远不会执行。
任务进入线程池的大体流程是,先创建核心线程去执行它,如果核心线程已超限,那么就把任务添加进队列;队列满了就创建非核心线程,非核心线程也超限了,则执行相关拒绝策略。
- private final class Worker
- extends AbstractQueuedSynchronizer
- implements Runnable
- {
-
- // 非重入锁
- protected boolean tryAcquire(int unused) {
- if (compareAndSetState(0, 1)) {
- setExclusiveOwnerThread(Thread.currentThread());
- return true;
- }
- return false;
- }
Worker包装了任务线程,主要是为了维护中断控制状态和其他次要状态记录,及任务的执行。Worker同时继承了AQS,在任务线程执行前lock,任务执行完unlock。加锁的目的主要是保护任务线程的执行,线程池唤醒一个任务线程等待任务,而不是中断当前正在执行任务的线程去执行任务。Worker使用了一个 非重入互质锁,而不是ReentrantLock,这样做的目的是以防在任务执行的过程,线程池控制方法的改变,对任务线程执行的影响,比如setCorePoolSize方法。另外为了防止任务线程在实际执行前被中断,我们初始化锁状态为-1,在runWorker方法中,我们会清除它。runWorker执行任务时,首先释放锁,此时锁打开,允许中断,如果线程池正在stop,确保线程池已中断,否则 做执行前工作,执行任务,做执行后工作,如果任务被中断,则工作线程数量减1; 如果任务完成,则更新完成任务数量,从工作任务集中移除工作线程,尝试结束线程池。
addWorker方法
从方法execute的实现可以看出: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; // addWorker失败,退出方法
-
- //状态符合跳球,死循环
- 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了。下面是可以进入到内循环的情况:
当进入到内循环后,会首先获取当前运行的线程数量。首先判断当前运行线程数量是否大于等于CAPACITYA(2^29-1),其次根据是否是核心线程与corePoolSize或者maximumPoolSize比较。所以线程的数量不会超过CAPACITY和maximumPoolSize的较小值。如果数量符合条件,那么就让ctl加1,然后跳出外部循环。如果线程数量达到了最大,那么回再判断当前状态,如果状态和之前的不一致了,那么继续外循环。下面是可以跳出外循环的情况:
一旦跳出外循环,表示可以创建创建线程,这里具体是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内部是一个死循环,首先判断状态,下面是跳出循环的情况:
如果不满足上述的情况,那么目前状态属于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提供了一个实现阻塞锁和其他同步工具,比如信号量、事件等依赖于等待队列的框架。Worker的构造方法中会使用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,然后继续尝试从队列中取出任务。( while (task != null || (task = getTask()) != null) 这里并发下会不会导致任务的重复执行呢?比如两个线程在这里拿到了同一个task,其中一个执行任务,另外一个等待获取锁再执行,答案肯定是不会,但没看明白这里是怎么实现的)下面看一下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的情况 :
上面是ThreadPoolExecutor调用execute方法提交任务后的执行流程,下面总结一下:
addWorker方法中会两个死循环,外循环检查线程池状态是否还可以接受新任务;内循环根据是否是核心线程与corePoolSize或maximumPoolSize比较,如果数量符合则创建线程,否则添加失败。
创建线程池一般都通过Executors的工厂方法创建线程,一般有四种线程,分别是FixedSizeThreadPoolc、SingleThreadPool、CachedThreadPool和ScheduledThreadPool。
FixedSizeThreadPoolExecutor只有核心线程,没有非核心线程,并且核心线程的数量固定。下面是创建该线程池的方法:
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- }
可以看到corePoolSize和maximumPoolSize均为nThreads参数,并且没有超时且队列是没有边界的。所以该线程池一旦开启,最多会有nThreads个线程,且线程一旦创建,就不会销毁(getTask方法使用workQueue.take()阻塞获取任务,runWorker会一直执行)。只要有任务提交,就会添加给核心线程或加入队列。
SingleThreadPool创建一个Worker线程操作一个无边界的队列。如果使用该线程池,那么所有提交的任务将会按照顺序被执行。
- public static ExecutorService newSingleThreadExecutor() {
- return new FinalizableDelegatedExecutorService
- (new ThreadPoolExecutor(1, 1,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>()));
- }
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就可以获取到任务,那么之前创建的线程也就可以得到复用。
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,非核心线程数2,非核心线程空闲结束时间5秒,阻塞队列大小8:
- public class ThreadPoolExecutorTest {
-
- public static void main(String[] args) throws InterruptedException {
- ThreadPoolExecutor pool = new ThreadPoolExecutor( 1, 2, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(8));
- for (int i = 1; i <=10; i++) {
- //创建线程类对象
- Test myTask = new Test(i);
- //开启线程
- pool.execute(myTask);
- //获取线程池中线程的相应参数
- System.out.println("任务"+ myTask.num + "进入线程池"+",线程池中线程数目场景1:" +pool.getPoolSize() + ",队列中等待执行的任务数目:"+pool.getQueue().size() + ",已执行完的任务数目:"+pool.getCompletedTaskCount());
- }
- Thread.sleep(2000);
- System.out.println("线程池中线程数目场景2:" +pool.getPoolSize() + ",队列中等待执行的任务数目:"+pool.getQueue().size() + ",已执行完的任务数目:"+pool.getCompletedTaskCount());
- Thread.sleep(5000);
- System.out.println("线程池中线程数目场景3:" +pool.getPoolSize() + ",队列中等待执行的任务数目:"+pool.getQueue().size() + ",已执行完的任务数目:"+pool.getCompletedTaskCount());
- Thread.sleep(5000);
- System.out.println("线程池中线程数目场景4:" +pool.getPoolSize() + ",队列中等待执行的任务数目:"+pool.getQueue().size() + ",已执行完的任务数目:"+pool.getCompletedTaskCount());
- pool.shutdown();
- System.out.println("线程池中线程数目场景5:" +pool.getPoolSize() + ",队列中等待执行的任务数目:"+pool.getQueue().size() + ",已执行完的任务数目:"+pool.getCompletedTaskCount());
-
- }
-
- static class Test implements Runnable {
-
- private int num;
-
- public Test(int num) {
- this.num = num;
- }
-
- @Override
- public void run() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName());
- }
- }
- }
打印结果:
- //任务1进来,创建一个核心线程,执行任务1
- 任务1进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:0,已执行完的任务数目:0
- 任务2进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:1,已执行完的任务数目:0
- 任务3进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:2,已执行完的任务数目:0
- 任务4进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:3,已执行完的任务数目:0
- 任务5进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:4,已执行完的任务数目:0
- 任务6进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:5,已执行完的任务数目:0
- 任务7进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:6,已执行完的任务数目:0
- 任务8进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:7,已执行完的任务数目:0
- //任务2-任务9共8个任务进入阻塞队列
- 任务9进入线程池,线程池中线程数目场景1:1,队列中等待执行的任务数目:8,已执行完的任务数目:0
- //阻塞队列已满,任务10进来,创建一个非核心线程去执行它,此时工作线程2
- 任务10进入线程池,线程池中线程数目场景1:2,队列中等待执行的任务数目:8,已执行完的任务数目:0
- // 如果将for循环10改成11,这里报异常,但前面10个任务照样进行
- pool-1-thread-2
- pool-1-thread-1
- // 队列中还有任务,工作线程为2
- 线程池中线程数目场景2:2,队列中等待执行的任务数目:6,已执行完的任务数目:2
- pool-1-thread-2
- pool-1-thread-1
- pool-1-thread-2
- pool-1-thread-1
- pool-1-thread-2
- pool-1-thread-1
- pool-1-thread-1
- pool-1-thread-2
- // 任务全部完成,非核心线程还未超过空闲时间,工作线程为2
- 线程池中线程数目场景3:2,队列中等待执行的任务数目:0,已执行完的任务数目:10
- // 任务全部完成,非核心线程超过空闲时间,工作线程为1
- 线程池中线程数目场景4:1,队列中等待执行的任务数目:0,已执行完的任务数目:10
- // 关闭线程池,工作线程为0
- 线程池中线程数目场景5:0,队列中等待执行的任务数目:0,已执行完的任务数目:10
非核心线程超时结束分析,getTask部分代码:
- /* 所有任务结束,此时工作线程大于核心线程wc > corePoolSize为true,
- 队列以非阻塞方法poll获取任务,队列为空,故r == null,timedOut = true,进入for下一次循环,
- 此时timed && timedOut为true,队列为空,执行compareAndDecrementWorkerCount方法,退出循环,线程结束*/
- 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;
- }
-
-
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。