说明:以ThreadPoolExecutor线程池为例说明整个流程(不同的线程池实现上略有差别)。


一、shutdown流程

1、流程简介

  • 修改线程池状态为SHUTDOWN
  • 不再接收新提交的任务
  • 中断线程池中空闲的线程
  • 第③步只是中断了空闲的线程,但正在执行的任务以及线程池任务队列中的任务会继续执行完毕

二、shutdownNow流程

1、流程简介

  • 修改线程池状态为STOP
  • 不再接收任务提交
  • 尝试中断线程池中所有的线程(包括正在执行的线程)
  • 返回正在等待执行的任务列表 List<Runnable>

此时线程池中等待队列中的任务不会被执行,正在执行的任务也可能被终止(为什么是可能呢?因为如果正常执行的任务如果不响应中断,那么就不会被终止,直到任务执行完毕)


三、问题说明

①、线程是如何中断的

中断线程池中的线程的方法是通过调用 Thread.interrupt()方法来实现的,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt() 方法是无法中断当前的线程的(因为sleep、condition、await这些是响应中断的)。所以,shutdownNow()并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的。

②、为什么修改线程池状态为shutdown以后线程池就不能接收新任务了

在向线程池提交任务的时候,会先检查线程池状态, 线程池状态为非关闭(或停止)时才能提交任务,这里已经将线程池状态修改为shutdown了,自然就不能接受新的任务提交了,可参考execute(Runnable command)逻辑和 addWorker逻辑)

1、execute方法
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();// 【 1 】、worker数量比核心线程数小,直接创建worker执行任务if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}// 【 2 】、worker数量超过核心线程数,任务直接进入队列。这里进入队列前先判断了线程池状态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);}//【 3 】、 如果线程池不是运行状态,或者任务进入队列失败,则尝试创建worker执行任务(即:线程阻塞队列满了但线程池中的线程数没达到最大线程数,// 则新开启一个线程去执行该任务)。// 这儿有3点需要注意:// 1. 线程池不是运行状态时,addWorker内部会判断线程池状态// 2. addWorker第2个参数表示是否创建核心线程// 3. addWorker返回false,则说明任务执行失败,需要执行reject操作else if (!addWorker(command, false))reject(command);
}
2、addWorker方法
// 新建一个worker
w = new Worker(firstTask);
int rs = runStateOf(ctl.get());// 这儿需要重新检查线程池状态,即线程池状态为shutdown以后不能再提交任务了
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {// 添加到工作线程集合中workers.add(w);int s = workers.size();// 更新largestPoolSize变量的值if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;
}

四、源码比较

1、shutdown源码

public void shutdown() {final ReentrantLock mainLock = this.mainLock;// 获取线程池的锁mainLock.lock();try {// 检查关闭进入许可checkShutdownAccess();// 将线程池状态改为SHUTDOWNadvanceRunState(SHUTDOWN);// 【中断线程池中空闲线程】,注意和下面的shutdownNow方法中的进行对比interruptIdleWorkers();// 留给定时任务线程池的钩子方法,这里没有实现,在定时任务线程池中有实现onShutdown(); // hook for ScheduledThreadPoolExecutor} finally {mainLock.unlock();}// ①、如果线程池状态为正在运行 或 已经是 TIDYING 状态以上了 或者  线程池状态为shutdown但是等待队列中还有任务,// 那么这个方法什么都不做,直接返回// ②、如何线程池中还有未被中断的线程,则这里会再次去中断他(并且利用中断传播 从等待队列中删除等待的worker)// ③、如果线程池的状态是shutdown,并且等待队列中已经没有任务了,那么此时会把线程池状态转换为 TIDYING,// 并唤醒所有调用awaitTermination()等待线程池关闭的线程tryTerminate();
}

2、shutdownNow源码

public List<Runnable> shutdownNow() {List<Runnable> tasks;// 获取线程池的锁final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {// 检查关闭进入许可checkShutdownAccess();// 将线程池状态改为STOPadvanceRunState(STOP);// 【中断所有线程】interruptWorkers();// 获取任务队列里未完成任务tasks = drainQueue();} finally {mainLock.unlock();}// ①、如果线程池状态为正在运行 或 已经是 TIDYING 状态以上了 或者  线程池状态为shutdown但是等待队列中还有任务,// 那么这个方法什么都不做,直接返回// ②、如何线程池中还有未被中断的线程,则这里会再次去中断他(并且利用中断传播 从等待队列中删除等待的worker)// ③、如果线程池的状态是shutdown,并且等待队列中已经没有任务了,那么此时会把线程池状态转换为 TIDYING,// 并唤醒所有调用awaitTermination()等待线程池关闭的线程tryTerminate();return tasks;
}

通过比较shutdown源码和shutdownNow的源码我们可以发现,这两个方法最大的不同在于中断线程的地方:

  • 首先,需要再次明确的一点是,中断线程并不是立即把这个线程停止,而是把这个线程的【中断状态】设置为true,表示有其他线程来中断过这个线程。
  • shutdown方法调用interruptIdleWorkers()方法中断的只是线程池中空闲的线程,那么也就说明线程池中正在工作的线程没有被中断,所以说正在工作的线程会继续执行完毕,并且正在工作的线程也会去任务队列中将已经提交的任务取出来并执行。
  • shutdownNow方法调用的是interruptWorkers()方法,该方法会逐一遍历线程池中的每一个线程(包括空闲线程和正在工作的线程)并且去中断他,所以说当调用shutdownNow方法的时候,所有线程都会被中断,等待队列中的任务不会被执行,正在执行的任务也可能会中断退出(为什么是可能而不是一定?因为如果正在执行的线程不响应中断,那么他就会继续运行)
  • 中断所有线程和中断空闲线程的代码比较,可以看源码interruptIdleWorkers()interruptWorkers()方法的实现,比较简单

上面代码中tryTerminate()方法值得探究也比较有趣,他先中断一个空闲线程,然后通过传播中断信号去中断其他的线程,然后不断地传播给每个worker!!!!

线程池shutdown和shutdownNow原理和区别相关推荐

  1. 关闭线程池 shutdown 和 shutdownNow 的区别

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:https://blog.csdn.net/xiewenfeng520/article/details/107013342 前 ...

  2. 关闭线程池 shutdown 和 shutdownNow 的区别?

    前言 本章分为两个议题 如何正确关闭线程池 shutdown 和 shutdownNow 的区别 1.线程池示例 public class ShutDownThreadPoolDemo {privat ...

  3. Java多线程 关闭线程池 shutdown() 、shutdownNow()、awaitTermination()

    目录 一.说明 二.理解 三.实现 1.shutdown() 2.shutdownNow() 3.awaitTermination() 一.说明 ThreadPoolExecutor 继承 Execu ...

  4. 线程池的shutdown()与shutdownNow()方法的区别

    老习惯先上结论: shutdown只是将线程池的状态设置为SHUTWDOWN状态,正在执行的任务会继续执行下去,没有被执行的则中断. 而shutdownNow则是将线程池的状态设置为STOP,正在执行 ...

  5. 关于线程池ExecutorService的shutdown()与shutdownNow()方法的区别

    2019独角兽企业重金招聘Python工程师标准>>> 问题 这两天被一个问题折腾着:一个线程池,可能会有上万个任务要执行.问题是,一旦最先运行的线程执行完,整个线程池就结束了,哪怕 ...

  6. 线程池作用和参数原理

    线程池的作用 减少资源的开销 减少了每次创建线程.销毁线程的开销. 提高响应速度 每次请求到来时,由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度. 提高线程的可管理性 线程是一种稀缺资 ...

  7. java 线程池的使用及原理(二):线程池的状态及证明

    线程的状态具有运行与关闭的状态,那么线程池也不例外.java线程池具有 5 种状态:Running.ShutDown.Stop.Tidying.Terminated. 线程池状态解析 1. Runni ...

  8. java 中线程池的种类,原理以及源码解析(1)

    java 中的线程池创建都是Executors 类中提供的方法,并且方法返回线程池对象. Executors 源码: // // Source code recreated from a .class ...

  9. CLR线程池的作用与原理浅析

    线程池是一个重要的概念.不过我发现,关于这个话题的讨论似乎还缺少了点什么.作为资料的补充,以及今后文章所需要的引用,我在这里再完整而又简单地谈一下有关线程池,还有.NET中各种线程池的基础.更详细的内 ...

最新文章

  1. 用ext_skel,实现一个PHP扩展,添加到PHP并调用
  2. Caused by: org.xml.sax.SAXParseException: 不允许有匹配 [xX][mM][lL] 的处理指令目标。
  3. tomcat进程意外退出的问题分析
  4. 大数运算(5)——大数除法(取模、取余)
  5. intx update task - IB_IBINTX_UPDATE
  6. 直观秒懂:这波动图全是泵,应有尽有!
  7. cv2.error: opencv(4.4.0)_【OpenCV 4开发详解】图像连通域分析
  8. linux五周第三次课(3月7日)笔记
  9. ubuntu18.04 ros 使用anaconda创建虚拟环境 python3.7安装 opencv-3.4.6,TensorFlow安装,notebook
  10. eclipse中创建最简单的maven结构的项目
  11. java中多重循环和break、continue语句
  12. 一些认识或对不清楚知识的猜想
  13. 使用JFlash烧写boot教程
  14. C语言pthread.h运用
  15. 计算机操作系统-整理
  16. u-boot之ARM920T的start.S分析
  17. html 链接到 appstore,如何在微信浏览器内打开App Store链接
  18. opencv 图像拼接和图像融合技术
  19. 利用开放的isbn查询Api接口录入图书信息,工作效率倍增
  20. cocos2d-x apk 打包路径太深

热门文章

  1. vue给单独页面添加背景颜色
  2. Kafka丢数据、重复消费、顺序消费的问题
  3. 使用Arduino制作一款数字键盘安全门锁
  4. java math tan_Java Math tan() 使用方法及示例
  5. 60深度学习在文本领域的应用666
  6. 三菱FX3U——ST编程点动与自锁
  7. 安徽师范大学计算机学院导师,安徽师范大学 数字计算机学院 耿焕同老师简介 联系方式 手机电话 邮箱...
  8. 最新综述:视频数据的无监督域适应
  9. 草莓采摘机器人——ROS导航(一) ROS Kinetic + Handsfree系统安装以及环境配置
  10. linux python 例子,初学python案例 字典