Executors功能如此强大,ThreadPoolExecutor功不可没(一)

Stella981
• 阅读 611

作为 Java 程序员,无论是技术面试、项目研发或者是学习框架源码,不彻底掌握 Java 多线程的知识,做不到心中有数,干啥都没底气,尤其是技术深究时往往略显发憷。

在 JDK1.5 以前,研发人员在面对线程频繁调度的场景,必须手动打造线程池,来节约系统开销(画外音:真是吃了不少苦头)。

从 JDK1.5 开始,Java 提供了一个 Excutors 工厂类来生产线程池,可以帮助研发人员有效的进行线程控制(画外音:不用造轮子啦,爽歪歪)。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

(配图释义:JDK 1.8 能用 Excutors 创建的线程池)

如上图示意,Excutors 提供了满足各种场景的线程池创建方式, Java 研发人员就不用苦逼哈哈的去造轮子啦,谁用谁爽。

但是,阿里开发规约明确强制研发人员:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

(配图释义:阿里巴巴Java开发手册,线程池创建规约)

不过,若经常关注源码的同学会发现,无论是 newFixedThreadPool() 方法、newSingleThreadExecutor() 方法,还是 newCachedThreadPool() 方法,其背后均使用了 ThreadPoolExecutor。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

(配图释义:JDK 1.8 能用 Excutors 创建的线程池的背后)

通过上面源码截图,可以清晰看出,以上几种创建线程池的方式,均是对 ThreadPoolExecutor 类的封装,所以要想彻底掌握线程池,势必要吃透线程池背后的 ThreadPoolExecutor。

1

解剖:构造函数

有关 ThreadPoolExecute 构造函数,很多书上或者文章都会提到,下面再简单了解一下每个参数的具体含义。

public ThreadPoolExecutor(int corePoolSize,

构造函数的参数释义:

corePoolSize:指定线程池中的线程数量; 

maximumPoolSize:指定线程池中的最大线程数量;

keepAliveTime:当线程池中线程数量超过 corePoolSize 时,空闲线程的存活时间; 

unit:keepAliveTime 的单位; 

workQueue:任务队列,存放提交尚未被执行的任务; 

threadFactory:线程工厂,用于创建线程,一般用默认的即可;

handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。

以上参数除了 workQueue 以及 handler 外,大部分都很易懂。接下来重点说说 workQueue 以及 handler 两个参数。

参数 BlockingQueue workQueue,是用于存放提交尚未被执行的任务的队列,类型是 BlockingQueue 接口的对象,用于存放 Runnable 对象。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

参数 RejectedExecutionHandler handler 是指当任务数量超过系统承载能力时,该如何处理?其中 JDK 提供了四种拒绝策略。

Executors功能如此强大,ThreadPoolExecutor功不可没(一) (配图释义:JDK 1.8 内置的拒绝策略)

JDK 提供的四种拒绝策略归纳,简单了解一下。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

2

思考:使用 Executors 会导致 OOM?

了解完 ThreadPoolExecutor 类的构造函数,接下来探讨一下阿里开发手册明确强制的一条使用线程池的规约。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

为了更清晰的认识,不妨走进源码看一看。首先走进 newFixedThreadPool() 方法的源码,一探究竟。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

如源码截图所示,newFixedThreadPool() 方法的实现,返回一个 corePoolSize 和 maximumPoolSize 大小一样的,并且使用了 LinkedBlockingQueue 任务队列的线程池。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

如上面 LinkedBlockingQueue 的源码所示,队列的默认长度为 Integer.MAX_VALUE,那么当任务提交频繁时,线程池中的线程处理不过来时,队列可能会迅速膨胀,从而会出现 OOM。

接着走进 newSingleThreadExecutor() 方法的源码,看看有没有新大陆。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

如源码截图示意,newSingleThreadExecutor 方法实现中,corePoolSize 和 maximumPoolSize 设置的值均为 1,返回一个单线程的线程池,并且使用 LinkedBlockingQueue 任务队列来存在提交的任务,与 newFixedThreadPool() 方法一样,当任务提交频繁时,线程池中的线程处理不过来时,队列会迅速膨胀,从而会出现 OOM。

最后看看 newCachedThreadPool() 方法的源码实现,一探究竟。

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

如上图源码示意,newCachedThreadPool() 方法实现,返回了一个 corePoolSize 为 0,maximumPoolSize 的值为 Integer.MAX_VALUE,并且使用 SynchronousQueue 作为任务队列的线程池。

而 SynchronousQueue 队列是一种直接提交的队列(不会保存提交的任务),所以总会使线程池增加新的线程来执行任务,当任务执行完毕后,由于 corePoolSize 为 0,因此空闲线程又会在 60 秒内被回收。

如果同时有大量任务被提交,而任务的执行又不那么快时,newCachedThreadPool() 方法,便会开启大量的线程进行处理,这样可能很快耗尽系统的资源,进而导致 OOM。

3

寄语写最后

本次,主要引入线程池背后的 ThreadPoolExecutor 类,算是正式开启探寻线程池背后的奥秘之旅,先有个初步的认识,知其然知其所以然,后续会逐步深入。

好了,本次就谈到这里, *一起聊技术、谈业务、喷架构,少走弯路,不踩大坑。会持续输出原创精彩分享,敬请期待!* Executors功能如此强大,ThreadPoolExecutor功不可没(一)

推荐阅读:

彻底搞懂 Java 线程池,干啥都不再发憷

Java程序跑的快,全要靠线程带

fastjson的这些坑,你误入了没?

真实|技术人员该如何站好最后一班岗?

Java 8 的这些特性,你知道吗?

改掉这些坏习惯,还怕写不出健壮的代码?(一)

改掉这些坏习惯,还怕写不出优雅的代码? (二)

改掉这些坏习惯,还怕写不出优雅的代码? (三)

改掉这些坏习惯,还怕写不出健壮的代码? (四)

改掉这些坏习惯,还怕写不出精简的代码? (五)

改掉这些坏习惯,还怕写不出精简的代码? (六)

坚持是一种信仰,在看是一种态度!

Executors功能如此强大,ThreadPoolExecutor功不可没(一)

本文分享自微信公众号 - 一猿小讲(yiyuanxiaojiangV5)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Executors相关的类(线程池)
一、概述Java是天生就支持并发的语言,支持并发意味着多线程,线程的频繁创建在高并发及大数据量是非常消耗资源的,因为java提供了线程池。在jdk1.5以前的版本中,线程池的使用是及其简陋的,但是在JDK1.5后,有了很大的改善。JDK1.5之后加入了java.util.concurrent包,java.util.concurrent包的加入给
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这