线程池ThreadPoolExecutor源码解析
来源:互联网 发布:淘宝上怎么换货流程 编辑:程序博客网 时间:2024/05/16 05:08
最近将ThreadPoolExecutor源码又读了一遍,将以前没有弄的太懂的地方给弄懂了点。所以写下这篇博客记录一下自己的理解。
在看源码之前得知道线程池中几个参数的作用和线程池大体的工作原理,这样才能更好的理解源码。
一.线程池的构造方法
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)corePoolSize:线程池的核心线程数,当线程池中的工作线程数小于核心线程数的时候,只要向线程池指派任务,线程池就会创建工作线程。
maximumPoolSize:线程池最大工作线程数,当线程池中的工作线程达到最大数的时候,即使再向线程池指派任务,线程池不会创建工作线程。
keepAliveTime:当线程池的工作线程数大于核心线程数的时候,多余的核心线程数的部分线程可以保持keepAliveTime的空闲时间,当keepAliveTime时间内还没有获取到任务,这些线程后就会被回收。
unit:保持空闲时间的时间单位。
workQueue:任务队列,当线程池里面核心线程都在工作的时候,再向线程池指派任务,线程池会将任务放入任务队列里,工作线程在执行完任务后会再向任务队列里取出任务来执行。
threadFactory:创建执行任务的工作线程的线程工厂。
handler:拒绝任务加入线程池的策越,当线程池里的线程已经达到最大数后,再向线程池里加派任务时,线程池会决绝执行这些任务,handler就是具体执行拒绝的对象。
线程池的大体工作思路
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize数的线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中核心线程空闲时间达到keepAliveTime也将关闭
你看到这里其实对线程池大体的工作原理就清仓了,如果你想研究源码可以继续往下看。
二. 线程池方法源码解读
1.参数解读
/* *可以将这个参数看成是一个三十二位的二进制数, *其中前三位表示线程池的状态, *后二十九位表示线程池中工作线程的数量 */private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));private static final int COUNT_BITS = Integer.SIZE - 3;//CAPACITY值为:00011111111111111111111111111111private static final int CAPACITY = (1 << COUNT_BITS) - 1;//RUNNING状态表示线程池可以接受任务正常工作private static final int RUNNING = -1 << COUNT_BITS;//SHUTDOWN状态表示线程池不接受任务,但如果阻塞队列中还有任务,会将阻塞队列中的任务执行完private static final int SHUTDOWN = 0 << COUNT_BITS;//STOP状态表示线程池不接受任务,也不会执行阻塞队列中的任务,即使阻塞队列中还存在任务private static final int STOP = 1 << COUNT_BITS;private static final int TIDYING = 2 << COUNT_BITS;private static final int TERMINATED = 3 << COUNT_BITS;//获取线程池的状态,一般会用形参rs表示,在后面rs参数一般表示线程池状态private static int runStateOf(int c) { return c & ~CAPACITY; }//获取线程池工作线程的数量,一般用形参wc表示private static int workerCountOf(int c) { return c & CAPACITY; }private static int ctlOf(int rs, int wc) { return rs | wc; }
2.execute方法
execute方法是线程池执行任务的入口,execute方法的入参是必须要实现了Runnable接口的实现类
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();/* *获取线程池中工作线程的数量和核心线程数做比较 *如果小于就调用addWorker方法, * *如果失败了会重新获取工作线程数量和线程池状态,成功就直接返回 **/if (workerCountOf(c) < corePoolSize) {/* * 此时调用addWorker方法就是创建一个新的工作线程来执行这个任务 * 向addWorker方法传入参数true,会在addWorker方法里面继续将工作线程数量和核心线程数做比较。 */if (addWorker(command, true))return;c = ctl.get();}/* * 程序运行到这里的前提条件有两个 * 1.工作线程数量大于核心线程数量 * 2.调用addWorker方法失败 * 如果线程池是running状态,而且任务加入阻塞队列成功,执行if了里面的代码 * if里面的代码其实是对加入到阻塞队列的任务的条件的一个后续判断,这句话比较绕 * 意思是将任务放到阻塞队列了后,我还要判断是否应该将这个队列放到阻塞队列中 * 这里为什么后续还要加判断我也不是很懂。干嘛不加在前面呢? */if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();/* * 重新判断线程池的工作状态,如果不是running状态,就将任务从队列中移除 * 移除后调用线程池的拒绝策越,默认的是抛出异常 */if (! isRunning(recheck) && remove(command))reject(command);/* * 执行到这的前提条件:线程池的状态是running * 如果将任务加入到了队列中,却发现线程池中已经没有工作线程了怎么办? * 我刚开始对线程池中没有工作线程还存在疑问,当allowCoreThreadTimeout设置成true的时候 * 核心线程有可能在空闲时间超时后被回收,所以线程池存在工作线程数为0的情况 * ,或者核心线程数设置为0也会出现这个情况 */else if (workerCountOf(recheck) == 0)/* * 传false将会在addWorker方法中判断线程池的工作线程数量和最大线程数量做比较 * 传一个空的任务,开启一个工作线程,但这个工作线程会发现当前的任务是空,然后会去队列中取任务 * 这样就避免了线程池的状态是running,而且队列中还有任务,但线程池却不执行队列中的任务 */addWorker(null, false);}/* * 程序执行到这的前提条件有两个: * 1.线程池的工作状态不是running * 2.任务加入到队列失败了 * 如果是第一种情况线程池的工作状态不是running了,那调用addWorker方法也会返回false, * 就会调用拒绝执行任务的策越 * 如果是第二种情况,加入队列失败,这说明队列已经满了,那调用addWorker方法,参数传fasle * 表示会将线程池的工作线程数量和最大线程数量比较,如果小于就会创建新的工作线程来执行这个任务 */else if (!addWorker(command, false))reject(command);}execute方法比较绕,不是很直观,因为它的判断条件有点多,我感觉写法比较怪异。在execute方法中没有看到要执行任务的地方,在这个方法里只是做了条件判断,如果满足
条件的话,就交给addWorker处理,并且根据判断条件的不同,传给addWorker方法的参数也不同。总结一下execute方法的执行逻辑
1.当工作线程数量小于核心线程数量的时候,会将任务交给addWorker方法,addWorker方法会创建新的线程来处理这个任务
2.当工作线程数量大于和核心线程数量并且线程池的工作状态是running的时候,会将任务放入到阻塞队列中
3.当阻塞队列已经满了,会将任务交给addWorker方法处理
4.当交给addWorker方法处理失败或是线程池的状态不是running的时候,会调用线程池的拒绝策越。
execute方法其实是线程池工作的一个大体思路,具体细节我们可以看看addWorker方法。
3.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()) * 所以在这里能进if,让addWorker返回false的情况有这样几种 * 1.当线程池的状态是stop * 2.当线程池的状态是shutdown的话,firstTask不为空 * 3.当线程池的状态是shutdown的话,队列是空的 * 以上三种情况返回false */ if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); /* * 当传回core传true的时候,比较当前线程池工作线程数和核心线程数做比较 * 当传回core传false的时候,比较当前线程池工作线程数和最大线程数做比较 * 如果当前线程数都是大于等于他们的,直接返回false */ if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; //利用cas函数增加线程池工作线程数,如果成功就直接跳出这两层循环 if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { final ReentrantLock mainLock = this.mainLock; //Worker是线程池的一个内部类,其实完成任务和从队列中取任务都是在Worker中完成的 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()) // 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(); } //当将任务放到任务队列(不同于阻塞队列)成功后,启动工作线程,执行firstTask任务 if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted;}可以看到任务的启动是在addWorker方法是生成一个新的工作线程来开启任务。Worker就将工作线程和任务封装到了自己内部,我们可以将Worker看成就是一个工作线程,至于Worker是如何执行任务和从阻塞队列中取任务,那就是Worker的事了
4.内部类Worker代码
/** * Class Worker mainly maintains interrupt control state for * threads running tasks, along with other minor bookkeeping. * This class opportunistically extends AbstractQueuedSynchronizer * to simplify acquiring and releasing a lock surrounding each * task execution. This protects against interrupts that are * intended to wake up a worker thread waiting for a task from * instead interrupting a task being run. We implement a simple * non-reentrant mutual exclusion lock rather than use * ReentrantLock because we do not want worker tasks to be able to * reacquire the lock when they invoke pool control methods like * setCorePoolSize. Additionally, to suppress interrupts until * the thread actually starts running tasks, we initialize lock * state to a negative value, and clear it upon start (in * runWorker). */private final class Worker extends AbstractQueuedSynchronizer implements Runnable{ // ... /** 工作线程 */ final Thread thread; /**任务 */ Runnable firstTask; /** * 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); } // ...}这里我只贴出了 Worker类中和本次分析有关的代码, 其他代码就省略掉了. Worker类内部还包含一个线程对象 thread 和一个 Runnable对象 firstTask. 而这个线程对象的创建过程见35行, 将Worker对象自身作为这个线程对象 thread的 Runnable参数传递给 thread的构造方法. 那么我们就可以知道, 如果要运行该线程, 也就是执行 thread.start(), 那么实际上就是要执行 thread所在的 Worker类中的 run()方法, 而从第40行又可以知道, Worker类中的 run()方法又是调用 runWorker(this); 这个方法的, 并将 thread所在的 Worker对象作为这个 runWorker()方法的参数. 简单来说就是:
启动一个 Worker对象中包含的线程 thread, 就相当于要执行 runWorker()方法, 并将该 Worker对象作为该方法的参数.
5.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 { //任务执行完后,getTask方法接着从队列中取任务 while (task != null || (task = getTask()) != null) { w.lock(); // 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 { //如果没有继承ThreadPoolExecutor实现这个方法,这个方法是没有执行动作的 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 { //如果没有继承ThreadPoolExecutor实现这个方法,这个方法是没有执行动作的 afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { //走到这里说明队列里的任务都已经被执行完,移除工作线程 processWorkerExit(w, completedAbruptly); }}
6.getTask方法
import java.util.concurrent.TimeUnit;/* * 从阻塞队列中取任务,取任务有三种情况发生 * 1.渠道任务并返回任务 * 2.没有取到任务,返回null,这个工作线程被回收 * 3.没有取到任务,阻塞在向阻塞队列取任务这里 * 第三点就是线程池中的空闲任务是如何存在的 */private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); //当线程池的工作状态是stop,就减少工作线程数,返回null //当线程池的工作状态是SHUTDOWN并且队列是空的时候,就减少工作线程数,返回null if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } boolean timed; // Are workers subject to culling? for (;;) { int wc = workerCountOf(c); /* * 当允许核心线程超时后被收回或者是工作线程数大于核心线程数 * 这两种情况下都是一定要回收工作线程的 */ timed = allowCoreThreadTimeOut || wc > corePoolSize; /* * 第一次执行这个语句的时候,是一定会进if里面,跳出里面这层循环的,因为初始化的timedOut=false * 当不进入这个循环,说明工作线程超时了,工作线程超时一般会返回null; */ if (wc <= maximumPoolSize && ! (timedOut && timed)) break; /* * 说明工作线程超时了,工作线程超时一般会返回null; * 减少工作线程数量 */ if (compareAndDecrementWorkerCount(c)) return null; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } try { /* * timed为true的时候,当工作线程取任务超时就会返回,返回后会被回收 * 当timed为false的时候,说明当前工作线程不需要被回收,所以就可以在向阻塞队列取任务的时候被阻塞 * 这里就提现了线程池中空闲的线程是如何存在的 */ Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; /* * 走到这里,r一定为空,最后会进入到第40行的if里面,还是返回null * 当返回null,这个工作线程就会被回收 * */ timedOut = true; } catch (InterruptedException retry) { timedOut = false; } }}
7.processWorkerExit方法
/** * 减少工作线程数,将工作线程从工作线程队列中移除 */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); } }
通过源码,不仅知道了线程池的工作原理,而且知道了工作线程是如何执行任务的,当工作线程空闲的时候,线程池是如何处理的。
参考文章:http://blog.csdn.net/clevergump/article/details/50688008
- 线程池ThreadPoolExecutor源码解析
- Java线程池源码解析(ThreadPoolExecutor)
- Java线程池ThreadPoolExecutor源码解析
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- 0031 Java线程池(ThreadPoolExecutor)JDK源码解析【基础】
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- JDK 源码解析 —— Executors ExecutorService ThreadPoolExecutor 线程池
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池ThreadPoolExecutor深度探索及源码解析
- Java线程池ThreadPoolExecutor深度探索及源码解析
- Java线程池ThreadPoolExecutor深度探索及源码解析
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java线程池ThreadPoolExecutor深度探索及源码解析
- 选择排序,插入排序,冒泡排序,希尔排序,归并排序,快速排序
- YUV转RGB
- Spring中@ControllerAdvice注解配合@ExceptionHandler实现统一异常处理
- JavaScript实现音频进度条
- EA&UML日拱一卒-状态图::状态
- 线程池ThreadPoolExecutor源码解析
- Cloudopt通过百度安全指数完美评价
- POJ 2135 Farm Tour
- Stay Hungry,Stay Foolish
- LeetCode 268. Missing Number
- 最全Pycharm教程
- android触摸屏事件学习
- IE9下Swiper控件不能准确定位到指定页面问题的解决
- Java并发编程:Lock