ThreadPoolExecutor实现原理
转载:https://blog.csdn.net/yanyan19880509/article/details/52718497
前言
做java开发的,一般都避免不了要面对java线程池技术,像tomcat之类的容器天然就支持多线程。即使是做偏后端技术,如处理一些消息,执行一些计算任务,也经常需要用到线程池技术。鉴于线程池技术的重要性,接下来会分多篇介绍java中提供的ThreadPoolExecutor线程池实现的底层机制。只有对机制了然于胸,才能更好驾驭这把利器。
线程池技术演示流程
关键概念:
如果说怎么最容易了解线程池的实现原理,那就是一步一步动态的演示。为了便于理解,这里先介绍几个线程池用到的概念。
ThreadPoolExecutor: 这是线程池实现类,会动态创建多个线程,并发执行提交的多个任务;
Worker: 是个Runnable实现类来的,内部会创建一个线程,一直循环不断执行任务,所以可以认为一个Worker就是一个工作线程;
corePoolSize: 当池中总线程数<corePoolSize时,提交一个任务创建一个新线程,而不管已经存在的线程是不是闲着,通常情况下,一旦线程数达
到了corePoolSize,那么池中总线程数是不会跌破到corePoolSize以下的。(除非 allowCoreThreadTimeOut=true并且keepAliveTime>0);
maximumPoolSize: 当池中线程数达到了corePoolSize,这时候新提交的任务就会放入等待队列中,一般情况下,这些任务会被前面创建的 corePoolSize个线程执行。当任务提交速度过快,队列满了,这时候,如果当前总线程数<maximumPoolSize,那么线程池会创建一个新的线程来执行新提交的任务,否则根据策略放弃任务;
keepAliveTime:存活时间,分两种情况: (1)allowCoreThreadTimeOut=true,所有线程,一旦创建后,在keepAliveTime时间内,如果没有任务可以执行,则该线程会退出并销毁,这样的好处是系统不忙时可以回收线程资源;(2)allowCoreThreadTimeOut=false,如果总线程数<=corePoolSize,那么这些线程是不会退出的,他们会一直不断的等待任务并执行,哪怕当前没有任务,但如果线程数>corePoolSize,而且一旦一个线程闲的时间超过
keepAliveTime则会退出,但一旦降低到corePoolSize,则不会再退出了。
allowCoreThreadTimeOut: 用于决定是否在系统闲时可以逐步回收所有的线程,如果为allowCoreThreadTimeOut=true,必须结合keepAliveTime一起使用,用于决定当线程数<corePoolSize时,是否要回收这些线程。
workQueue:这是一个阻塞队列,当线程数>=corePoolSize,这时候提交的任务将会放入阻塞队列中,如果阻塞队列是无界的,那么总的线程数是不可能>corePoolSize的,即maximumPoolSize属性就是无用的;如果阻塞队列是有界的,而且未满,则任务入队,否则根据maximumPoolSize的值判断是要新建线程执行新任务或者是根据策略丢弃任务。
有了以上的概念,接下来将根据 allowCoreThreadTimeOut的值分两种场景进行演示说明。
演示一: allowCoreThreadTimeOut=true
1、 初值设定: corePoolSize=2; maximumPoolSize=3;keepAliveTime=10s; workQueue容量为2; 初始状态图如下所示:
2、submit一个任务A,由于总线程数0<corePoolSize; 此时会创建一个线程执行任务A,状态图如下:
3、submit一个任务B,由于总线程数1<corePoolSize,此时会创建一个线程执行任务B,状态图如下:
4、submit一个任务C,由于总线程数 2=corePoolSize,workQueue不满,这时候任务C入队列,状态图如下:
5、submit一个任务D,很明显,任务D入队列,状态图如下:
6、submit一个任务E,这时候,线程数2=corePoolSize,workQueue也已经满了,判断发现线程数2<maximumPoolSize,所以继续创建线程执行任务E,状态图如下:
7、submit一个任务F,这时候,2=corePoolSize,workQueue已满,判断发现线程数3=maximumPoolSize,这种情况下,线程池会根据策略来决定是否要放弃当前任务,或者是把workQueue中一个任务删除,然后入队新的任务,也可以自定义策略,比如,持久化到DB之类的,或者是发出警报。我们假设是直接丢弃策略,这时候状态图不变。
8、这会没有新任务到来了,各个任务陆续执行完了,包括队列中的C和D也执行完了,这时候,由于当前场景为allowCoreThreadTimeOut=true,如果在等待keepAliveTime时间后线程仍旧无法获取新的任务,线程将会自行退出,这将导致最终所有线程都退出了,也就是又再次回到了原始状态,如下图所示:
说得更简单一些就是:在 allowCoreThreadTimeOut=true时,如果一个线程等了keepAliveTime还无法获取新任务,则退出。
演示二:allowCoreThreadTimeOut=false
1~7 的步骤与状态跟“演示一”是一样的,所以这里不再赘述,这时候状态图如下:
8、这会没有新任务到来了,各个任务陆续执行完了,包括队列中的C和D也执行完了,这时候,由于当前场景为allowCoreThreadTimeOut=false,并且线程数3>corePoolSize,这时候每个线程都感知到线程数过多,所以它们都会尝试把自己停止掉,实现中最终只会停止一个线程,剩余线程数2=corePoolSize,接下来,哪怕一直没有新任务来,这corePoolSize个线程也不会退出,一直存活着等待接收任务。这时候,状态图如下:
线程池的状态
前一部分演示了线程池的基本实现原理,这一小节介绍一下线程池的状态,线程池概括上讲有5种状态,如下图所示:
RUNNING状态:
创建线程池的时候,线程池的初始状态为 RUNNING,接着就可以提交任务执行了。
SHUTDOWN状态:
当在RUNNING状态调用shutdown()时,线程池状态会被改为SHUTDOWN,这时候,submit任务的时候,会被拒绝,可以使用多种拒绝策略,
比如最简单就是直接丢弃任务。至于正在执行中的线程,会继续执行,同时会把阻塞队列中的任务也一并执行完毕,等到全部任务执行完毕,线程池会进入 TIDYING状态,等执行钩子方法terminated()之后,就会进入最终状态TERMINATED,这时候,整个线程池完全终止。
STOP状态:
当在RUNNING状态调用shutdownNow()时,线程池状态会被改为STOP,这时候,submit任务会被拒绝,那么如果有任务执行到一半,该怎么处理?其实,执行shutdownNow()时,会中断各个工作线程,所以任务会如何执行要看任务做的是什么事情,有没有处理中断异常。而阻塞队列如何有任务,这些任务将不会再执行,shutdownNow()执行后,将会返回阻塞队列中的未执行的任务列表。
TIDYING状态:
TIDYING只是一个过渡状态,当所有工作线程都停止后,线程池的状态会进入TIDYING,然后执行一个钩子方法terminated(),最后线程池会进入TERMINATED状态。
TERMINATED状态:
线程池终止状态,这个没什么可说的了,大家都明白。
总结
理解线程池最主要是要理解线程池几个主要的配置参数,如果不看实现细节,原理还是比较简单的。在了解原理的基础上再去看代码,就会事半功倍。
ThreadPoolExecutor实现原理的更多相关文章
- JDK ThreadPoolExecutor核心原理与实践
一.内容概括 本文内容主要围绕JDK中的ThreadPoolExecutor展开,首先描述了ThreadPoolExecutor的构造流程以及内部状态管理的机理,随后用大量篇幅深入源码探究了Threa ...
- jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)
jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...
- 21.线程池ThreadPoolExecutor实现原理
1. 为什么要使用线程池 在实际使用中,线程是很占用系统资源的,如果对线程管理不善很容易导致系统问题.因此,在大多数并发框架中都会使用线程池来管理线程,使用线程池管理线程主要有如下好处: 降低资源消耗 ...
- java线程池系列(1)-ThreadPoolExecutor实现原理
前言 做java开发的,一般都避免不了要面对java线程池技术,像tomcat之类的容器天然就支持多线程. 即使是做偏后端技术,如处理一些消息,执行一些计算任务,也经常需要用到线程池技术. 鉴于线程池 ...
- 线程池ThreadPoolExecutor实现原理
线程属于稀缺资源,对于线程的创建规则,引用<阿里巴巴 Java 手册>中的一条进行说明. 本篇从源码方面介绍ThreadPoolExecutor对象,并简要解析线程池工作原理. 首先Thr ...
- 线程池ThreadPoolExecutor工作原理
前言 工作原理 如果使用过线程池,细心的同学肯定会注意到,new一个线程池,但是如果不往里面提交任何任务的话,main方法执行完之后程序会退出,但是如果向线程池中提交了任务的话,main方法执行完毕之 ...
- Java8线程池ThreadPoolExecutor底层原理及其源码解析
小侃一下 日常开发中, 或许不会直接new线程或线程池, 但这些线程相关的基础或思想是非常重要的, 参考林迪效应; 就算没有直接用到, 可能间接也用到了类似的思想或原理, 例如tomcat, jett ...
- 线程池基本使用和ThreadPoolExecutor核心原理讲解
原文地址:https://www.jianshu.com/p/ec5b8cccd87d java和spring都提供了线程池的框架 java提供的是Executors: spring提供的是Threa ...
- JUC回顾之-ThreadPoolExecutor的原理和使用
Spring中的ThreadPoolTaskExecutor是借助于JDK并发包中的java.util.concurrent.ThreadPoolExecutor来实现的.基于ThreadPoolEx ...
随机推荐
- 洛谷P2762 太空飞行计划问题(最小割)
传送门 我们可以把实验放在左边,仪器放在右边,点有点权,然后连对应的有向边,就是求一个最大权闭合图,可以转化为最小割来做(关于这具体是个啥……可以百度胡伯涛<最小割模型在信息学竞赛中的应用> ...
- 【java】AtomicReference介绍
本文转载自:http://www.cnblogs.com/skywang12345/p/3514623.html 概要 本章对AtomicReference引用类型的原子类进行介绍.内容包括: Ato ...
- 八大排序算法的python实现(三)冒泡排序
代码: #coding:utf-8 #author:徐卜灵 #交换排序.冒泡排序 L = [1, 3, 2, 32, 5, 4] def Bubble_sort(L): for i in range( ...
- 1.Java 程序工作原理
JVM:JAVA虚拟机,java程序运行在jvm上,jvm是java程序的运行环境. java程序的平台无关性:java编写的程序(.java)经过编译器变异成字节码文件(.class).这个字节码文 ...
- window.open()弹出窗口被拦截
之前有个需求是输入一些配置,然后点击预览,通过接口保存配置并返回预览页面链接,在新页面中打开链接.后来测试一直说没有新页面打开,我一看,原来是被浏览器拦截了. 原因如下: 浏览器只有在认为click和 ...
- C# 精准计时之 QueryPerformanceCounter QueryPerformanceFrequency用法
C# 用法: public static class QueryPerformanceMethd { [DllImport("kernel32.dll")] public exte ...
- ubuntu15.04下安装docker
##获得更多资料欢迎进入我的网站或者 csdn或者博客园 最近听说docker很火,不知道什么东西,只知道是一个容器,可以跨平台.闲来无事,我也来倒弄倒弄.本文主要介绍:ubuntu下的安装,以及基 ...
- Query on a tree II 倍增LCA
You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, ...
- java基础_02
一.this和super 作用: this:区分成员变量和局部变量 super:区分父类的成员变量和局部变量 用法: this.成员变量名://访问本类的成员变量 this.成员方法名()://访问本 ...
- Domoticz 接入苹果的 HomeKit 实现 Siri 控制
前言 接上次的折腾,这次尝试将 Domoticz 接入到苹果的 HomeKit,也就是在 iPhone 的 Siri 中可以语音控制.参考官方文档 步骤 安装 nodejs curl -sL http ...