java线程池系列(1)-ThreadPoolExecutor实现原理
前言
做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状态:
线程池终止状态,这个没什么可说的了,大家都明白。
总结
理解线程池最主要是要理解线程池几个主要的配置参数,如果不看实现细节,原理还是比较简单的。在了解原理的基础上再去看代码,就会事半功倍。
java线程池系列(1)-ThreadPoolExecutor实现原理的更多相关文章
- 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分
这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...
- 《java.util.concurrent 包源码阅读》12 线程池系列之ThreadPoolExecutor 第二部分
接着说worker线程是如何工作的.ThreadPoolExecutor有一个成员类叫Worker,所起到的作用就是线程池worker线程的作用. private final class Worker ...
- 《java.util.concurrent 包源码阅读》11 线程池系列之ThreadPoolExecutor 第一部分
先来看ThreadPoolExecutor的execute方法,这个方法能体现出一个Task被加入到线程池之后都发生了什么: public void execute(Runnable command) ...
- Java 线程池的介绍以及工作原理
在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.将需处理的任务的数量大 使用线程池的好处: 1. 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗.2. 提高响应速度: ...
- Java线程池带图详解
线程池作为Java中一个重要的知识点,看了很多文章,在此以Java自带的线程池为例,记录分析一下.本文参考了Java并发编程:线程池的使用.Java线程池---addWorker方法解析.线程池.Th ...
- 并发编程系列:Java线程池的使用方式,核心运行原理、以及注意事项
并发编程系列: 高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景 线程池的缘由 java中为了提高并发度,可以使用多线程共同执行,但是如果有大量线程短时间之内被创建和销毁,会占用大量的 ...
- Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- Java 线程池(ThreadPoolExecutor)原理分析与使用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
- Java 线程池(ThreadPoolExecutor)原理解析
在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...
随机推荐
- 再探canvas(小球实例)
之前学习过canvas的一些使用,也用过canvas绘制过时钟, 但是很久不用,有些遗忘了,这里做一个简单的回顾. 在web页面创建一个canvas画布非常简单,如下即可: <canvas id ...
- Http编程之HttpClient
在Android开发中,Android SDK附带了Apache的HttpClient,它是一个完善的客户端.它提供了对HTTP协议的全面支持,可以使用HttpClient的对象来执行HTTP GET ...
- [ElasticSearch] 如何使用中文分詞ik與繁簡轉換stconvert插件
一. 環境安裝 ElasticSearch(以下簡稱ES)安裝已經算相對簡單了, 但要使用需要配置的插件, 對剛入門的新手還是有點麻煩, 所幸medcl大神提供一個配置好的ES-rtf版本, 新手們在 ...
- 触发Full GC的时机
由于Full GC的耗时是Minor GC的十倍左右,所以Full GC的频率设计得比Minor GC低得多.现总结一下触发Full GC的情况. 在那些实现了CMS的比较新的虚拟机中,如果配置了-X ...
- matlab实现M/M/1排队系统
Matlab实现. 分为主函数 MyLine 和被调用函数 Func. 主函数 MyLine 实现在 Func 函数的基础上实现序贯法, 将平均等待队长作为每次模拟的 X,求出置信区间.Func 函数 ...
- Nginx+Keepalived配置
1. Nginx安装 (1) 环境:分别在2台服务器上部署nginx且步骤一致: 如192.138.86.1和192.138.86.2 (2) 下载官网最新稳定版,地址:https://nginx.o ...
- Ubuntu通过xinput禁用及启用联想笔记本的触摸板
查看设备列表 通过xinput先查看一些都有哪些设备 xinput #或者 xinput list 显示结果如下 ddd@ddd:~$ xinput list Virtual core p ...
- HDU 5313——Bipartite Graph——————【二分图+dp+bitset优化】
Bipartite Graph Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)T ...
- openlayers 3 读取展示shp文件
简单的思路如下: 1.在arcgis中获得shp文件 2.将其转成geojson文件 3.用openlayers进行展示 第一步直接省略 第二步这里推荐一个shp转geojson很方便的工具网站htt ...
- VB.Net遍历已安装的程序卸载信息
Private Shared Function ProgramExists(ByVal pgName As String) As Boolean Dim temp As String = Nothin ...