Java并发(5)深入分析java线程池框架及实现原理(二)
来源:互联网 发布:7.62口径秃鹰数据 编辑:程序博客网 时间:2024/06/14 14:33
在上一篇文章中介绍了Java线程池框架的一些原理及基本架构,本篇文章主要介绍Java8中线程池框架ThreadPoolExecutor的工作原理!
ThreadPoolExecutor作为Java线程池框架最核心的一个类,它是线程池框架的具体实现,在上一篇文章中已经讲解了该类的构造方法及构造方法中各参数的具体含义,接下来分析ThreadPoolExecutor如何给任务分配线程并且执行任务的:
通常使用线程池框架会使用execute(Runnable command)将任务提交给线程池处理,execute是ThreadPoolExecutor类的主要方法,该方法给任务分配线程,先来看看在jdk1.8(本文中jdk源码均为jdk1.8)中该方法的实现:
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); //通过传入的拒绝处理策略进行处理}workerCountOf方法可以获得当前正在运行的线程数,先来看看workerCountOf方法和ctl到底是什么:
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; //不可接收新任务,可执行等待队列里的任务// 所有状态左移29位占高3位
private static final int STOP = 1 << COUNT_BITS; //不可接收新任务,不可执行等待队列里的任务,并且尝试终止所有在运行中的任务private static final int TIDYING = 2 << COUNT_BITS; //所有任务已终止,执行terminated()private static final int TERMINATED = 3 << COUNT_BITS; //terminated执行完成
// Packing and unpacking ctlprivate 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; }
在ThreadPoolExecutor类中有以上属性和方法,首先Integer.SIZE是32位,COUNT_BITS占32-3=29位,线程池工作线程数量占低29位,线程池状态占高3位,所以workerCountOf方法通过将c与CAPACITY进行与运算取出c中存取的低29位工作线程数量,runStateOf方法则取出整数c中高3位的线程池状态,理解了c中存储的数据结构再来看看c从何而来,从execute方法中可以看到,c=ctl.get(); 那么ctl是什么,从上面的源码看到,ctl是一个ThreadPoolExecutor的成员变量,并且该变量是一个原子的线程安全的变量,该变量在初始化的时候调用ctlOf(RUNNING,0)可以看出ctl初始化时高3位存储的状态是RUNNING,低29位存储的工作线程数量为0。总的来说,ctl中包含了两个值:高3位的线程池状态和低29位的工作线程数量,至于为什么要将两个数包装在一个ctl里面暂无所知,线程池状态的含义已标注在源码中。
在理解了ctl之后再回到ThreadPoolExecutor的execute方法中,可以看到,当线程池中工作线程小于corePoolSize时,二话不说直接创建一个线程,addWorker方法就是创建线程执行任务;如果工作线程不小于corePoolSize并且线程池状态为RUNNING那么将任务offer进阻塞队列,添加成功后检查一下在入队过程中ctl有没有发生过改变,如果状态忽然变为非RUNNING状态,将任务出队并将任务交给拒绝策略处理,如果线程池工作线程数量为0,则新建一个worker;第三种情况是当任务入队失败表明阻塞队列已满,此时将调用addWorker(command, false)创建一个非核心线程,注意此时addWorker第二个参数传入的是false 该参数表示创建的是否为核心线程,当传入false时创建的是非核心线程,如果创建失败表明已达到线程池的最大线程数,此时调用reject将任务提交给拒绝策略(RejectExecutionHandler)处理。至此execute的主线逻辑已经梳理完成,接下分析一下前面一直提到的addWorker是怎么实现的:
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) //必须:1.当前状态为RUNNING 或者 2.状态为SHUTDOWN(不接收新任务但是会执行阻塞队列中的任务)并且传入任务为空并且阻塞队列不为空(此时会从阻塞队列中获取任务),否则直接返回false return false; for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) //线程数量超标则返回false(逻辑很简单) return false; if (compareAndIncrementWorkerCount(c)) //CAS操作,让线程数量加1,成功就退出循环,失败是因为CAS失败(CAS是乐观锁的一种实现) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) //如果检查出线程池状态在线程数量CAS失败过程中发生改变,则回到最初重新循环 continue retry; // else CAS failed due to workerCount change; retry inner loop 如果线程数量CAS失败并且状态未发生改变 则重新CAS } } boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); //创建一个Worker类,该类是一个ThreadPoolExecutor的内部类,该类会在下文详细分析 final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. 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); //将Worker添加到workers中 workers是一个HashSet,表示运行中的线程 int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; //标志添加成功为true } } finally { mainLock.unlock(); } if (workerAdded) { //如果添加成功 则启动线程,并且设置workerStarted为true t.start(); workerStarted = true; } } } finally { if (! workerStarted) //如果workerStarted为false表示没有成功创Worker程并启动线程,调用失败处理函数 addWorkerFailed(w); } return workerStarted;}addWorker方法很长 但逻辑十分简单,具体逻辑已经标注在代码中,这里不在赘述,关于注释中提到的几个方法,一一列出:
首先是compareAndInCrementWorkerCount方法:典型的CAS操作
private boolean compareAndIncrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect + 1);}Worker类:该类继承自AQS(AbstractQueuedSynchronizer)并且实现了Runnable接口,可以看出Worker类本身也是一个Runnable,所以Worker可以理解为在提交的任务外面包了一层,那么在这包的一层里面主要干了些什么,来看看源码:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{ /** * This class will never be serialized, but we provide a * serialVersionUID to suppress a javac warning. */ 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类的整个源码,首先看构造方法,在构造方法中先将任务赋值给自己的一个成员变量,然后通过上一篇文章提到的ThreadFactroy创建一个线程,注意:newThread(this)方法传入的不是firstTask任务而是this,将自己当成Runnable传入,所以线程执行的是Worker类的run方法(前面提到Worker类实现了Runnable接口),那么来看看worker类的run方法,run调用了runWorker并传入了this,接下来看看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 { while (task != null || (task = getTask()) != null) { //当task为空时尝试从阻塞队列中取出任务,getTask()方法作用就是从阻塞队列中取出任务 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()) //如果线程池状态为stop或以上则中断线程 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); //任务执行之后退出之前调用 主要做一些统计和清理工作,清理统计完成之后会代码见下 }}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)) { //线程池状态为RUNNING或SHUTDOWN if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; //如果allowCoreThreadTimeOut为true则min为0,否则为corePoolSize,allowCoreThreadTimeOut默认为false表示核心线程永远不会死亡 if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false); //当传入的task为null时,根据前面的代码runWorker时会通过getTask尝试从阻塞队列中获取任务,所以在此处调用该方法的作用是当线程执行完一个任务时候马上去取阻塞队列中的任务接着执行 }}可以看出 在任务外面包的一层Worker主要做一些中断处理 统计 清理等工作并且执行阻塞队列中的下一个任务,介绍完Worker之后再回到之前的addWorker,在addWorker失败会调用addWorkerFailed方法:
private void addWorkerFailed(Worker w) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (w != null) workers.remove(w); decrementWorkerCount(); //工作线程数量减一 tryTerminate(); //尝试终止线程池 } finally { mainLock.unlock(); }}
tryTerminate() 执行流程:
1、判断线程池是否需要进入终止流程(只有当shutdown状态+workQueue.isEmpty 或 stop状态,才需要)
2、判断线程池中是否还有线程,有则 interruptIdleWorkers(ONLY_ONE) 尝试中断一个空闲线程(正是这个逻辑可以再次发出中断信号,中断阻塞在获取任务的线程)
3、如果状态是SHUTDOWN,workQueue也为空了,正在运行的worker也没有了,开始terminated
会先上锁,将线程池置为tidying状态,之后调用需子类实现的 terminated(),最后线程池置为terminated状态,并唤醒所有等待线程池终止这个Condition的线程
至于具体实现,放在下一篇文章中进行,下一篇文章将分析ThreadPoolExecutor如何终止线程池。
总结一下:
当任务通过execute方法提交给线程池时,会经历一下几部:
1.首先判断线程池状态是否为RUNNING,如果不为RUNNING,将直接拒绝任务
2.如上一篇文章所讲,判断核心线程数和最大线程数以及阻塞队列是否满了并做出相应的策略,达到最大线程之后的任务会被交由拒绝处理策略处理
3.通过addWorker方法将任务包装成Worker,并通过ThreadFactroy创建线程并启动线程,并且把运行中的worker放入workers的HashSet中
4.worker包装的任务执行完之后会将该worker从workers中移除,并且worker会去阻塞队列中取下一个任务重复第3部直到所有任务执行完成
5.所有任务执行完成之后worker会调用terminated方法终止线程池
*关于线程池如何终止将放在下一篇文章
- Java并发(5)深入分析java线程池框架及实现原理(二)
- Java并发(4)深入分析java线程池框架及实现原理(一)
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 深入分析java线程池的实现原理
- 算法分析(二)归并排序原理及java实现
- java多线程-专题-聊聊并发(一)深入分析Volatile的实现原理
- Linux下安装Tomcat服务器和部署Web应用
- PHP连接SQL SERVER 数据库 PHP连接MYSQL数据库并解决中文乱码问题。
- CAS单点登录-密码管理(十三)
- 【MySQL】查看mysql实时运行sql的工具–orztop
- virsh通过xml 创建虚拟机
- Java并发(5)深入分析java线程池框架及实现原理(二)
- ajax跨域获取php文件
- linux每天一命令: 用户组
- 阿里巴巴Java开发规约的IDEA插件使用
- 获取Android系统分享列表
- JDBI
- nginx 集群系列
- 【持续集成】使用Travis CI来持续集成你的开源项目
- Oracle用户登录