Executors功能如此强大,ThreadPoolExecutor功不可没(一)
作为 Java 程序员,无论是技术面试、项目研发或者是学习框架源码,不彻底掌握 Java 多线程的知识,做不到心中有数,干啥都没底气,尤其是技术深究时往往略显发憷。
在 JDK1.5 以前,研发人员在面对线程频繁调度的场景,必须手动打造线程池,来节约系统开销(画外音:真是吃了不少苦头)。
从 JDK1.5 开始,Java 提供了一个 Excutors 工厂类来生产线程池,可以帮助研发人员有效的进行线程控制(画外音:不用造轮子啦,爽歪歪)。
(配图释义:JDK 1.8 能用 Excutors 创建的线程池)
如上图示意,Excutors 提供了满足各种场景的线程池创建方式, Java 研发人员就不用苦逼哈哈的去造轮子啦,谁用谁爽。
但是,阿里开发规约明确强制研发人员:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式。
(配图释义:阿里巴巴Java开发手册,线程池创建规约)
不过,若经常关注源码的同学会发现,无论是 newFixedThreadPool() 方法、newSingleThreadExecutor() 方法,还是 newCachedThreadPool() 方法,其背后均使用了 ThreadPoolExecutor。
(配图释义:JDK 1.8 能用 Excutors 创建的线程池的背后)
通过上面源码截图,可以清晰看出,以上几种创建线程池的方式,均是对 ThreadPoolExecutor 类的封装,所以要想彻底掌握线程池,势必要吃透线程池背后的 ThreadPoolExecutor。
1
解剖:构造函数
有关 ThreadPoolExecute 构造函数,很多书上或者文章都会提到,下面再简单了解一下每个参数的具体含义。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
构造函数的参数释义:
corePoolSize:指定线程池中的线程数量;
maximumPoolSize:指定线程池中的最大线程数量;
keepAliveTime:当线程池中线程数量超过 corePoolSize 时,空闲线程的存活时间;
unit:keepAliveTime 的单位;
workQueue:任务队列,存放提交尚未被执行的任务;
threadFactory:线程工厂,用于创建线程,一般用默认的即可;
handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。
以上参数除了 workQueue 以及 handler 外,大部分都很易懂。接下来重点说说 workQueue 以及 handler 两个参数。
参数 BlockingQueue<Runnable> workQueue,是用于存放提交尚未被执行的任务的队列,类型是 BlockingQueue 接口的对象,用于存放 Runnable 对象。
参数 RejectedExecutionHandler handler 是指当任务数量超过系统承载能力时,该如何处理?其中 JDK 提供了四种拒绝策略。
(配图释义:JDK 1.8 内置的拒绝策略)
JDK 提供的四种拒绝策略归纳,简单了解一下。
2
了解完 ThreadPoolExecutor 类的构造函数,接下来探讨一下阿里开发手册明确强制的一条使用线程池的规约。
为了更清晰的认识,不妨走进源码看一看。首先走进 newFixedThreadPool() 方法的源码,一探究竟。
如源码截图所示,newFixedThreadPool() 方法的实现,返回一个 corePoolSize 和 maximumPoolSize 大小一样的,并且使用了 LinkedBlockingQueue 任务队列的线程池。
如上面 LinkedBlockingQueue 的源码所示,队列的默认长度为 Integer.MAX_VALUE,那么当任务提交频繁时,线程池中的线程处理不过来时,队列可能会迅速膨胀,从而会出现 OOM。
接着走进 newSingleThreadExecutor() 方法的源码,看看有没有新大陆。
如源码截图示意,newSingleThreadExecutor 方法实现中,corePoolSize 和 maximumPoolSize 设置的值均为 1,返回一个单线程的线程池,并且使用 LinkedBlockingQueue 任务队列来存在提交的任务,与 newFixedThreadPool() 方法一样,当任务提交频繁时,线程池中的线程处理不过来时,队列会迅速膨胀,从而会出现 OOM。
最后看看 newCachedThreadPool() 方法的源码实现,一探究竟。
如上图源码示意,newCachedThreadPool() 方法实现,返回了一个 corePoolSize 为 0,maximumPoolSize 的值为 Integer.MAX_VALUE,并且使用 SynchronousQueue 作为任务队列的线程池。
而 SynchronousQueue 队列是一种直接提交的队列(不会保存提交的任务),所以总会使线程池增加新的线程来执行任务,当任务执行完毕后,由于 corePoolSize 为 0,因此空闲线程又会在 60 秒内被回收。
如果同时有大量任务被提交,而任务的执行又不那么快时,newCachedThreadPool() 方法,便会开启大量的线程进行处理,这样可能很快耗尽系统的资源,进而导致 OOM。
3
寄语写最后
本次,主要引入线程池背后的 ThreadPoolExecutor 类,算是正式开启探寻线程池背后的奥秘之旅,先有个初步的认识,知其然知其所以然,后续会逐步深入。
本文分享自微信公众号 - 一猿小讲(yiyuanxiaojiangV5)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
JVM系列之:JIT中的Virtual Call
简介 什么是Virtual Call?Virtual Call在java中的实现是怎么样的?Virtual Call在JIT中有没有优化? 所有的答案看完这篇文章就明白了。 Virtual Call和它的本质 有用过PrintAssembly的朋友,可能会在反编译的汇编代码中发现有些方法调用的说明是invokevirtual,实际上这个invokevirtual就是Virtual Call。 Virtual Call是什么呢? 面向对象的编程语言基本上都支持方法的重写,我们考虑下面的情况: private static class CustObj { public void methodCall() { if(System.currentTimeMillis()== 0){ System.out.println("CustObj is very good!"); } } } private static class CustObj2 extends CustObj { public final void methodCall() { if(System.currentTimeMilli...
- 下一篇
一文讲透 “进程、线程、协程”
本文从操作系统原理出发结合代码实践讲解了以下内容: 什么是进程,线程和协程? 它们之间的关系是什么? 为什么说Python中的多线程是伪多线程? 不同的应用场景该如何选择技术方案? ... 什么是进程 进程-操作系统提供的抽象概念,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。程序本身是没有生命周期的,它只是存在磁盘上的一些指令,程序一旦运行就是进程。 当程序需要运行时,操作系统将代码和所有静态数据记载到内存和进程的地址空间(每个进程都拥有唯一的地址空间,见下图所示)中,通过创建和初始化栈(局部变量,函数参数和返回地址)、分配堆内存以及与IO相关的任务,当前期准备工作完成,启动程序,OS将CPU的控制权转移到新创建的进程,进程开始运行。 操作系统对进程的控制和管理通过PCB(Processing Control Block),PCB通常是系统内存占用区中的一个连续存区,它存放着操作系统用于描述进程情况及控制进程运行所需的全部信息(进程标识号,进程状态,进程优先级,文件系统指针以及各个寄存器的内容等),进程的PCB是系统感...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8安装Docker,最新的服务器搭配容器使用
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS8编译安装MySQL8.0.19
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音