深入理解 Java 线程池

2018 年 12 月 12 日 ImportNew

(点击上方公众号,可快速关注)


来源:虾扯人生


今天写代码的时候,用到了线程池,但是由于资源有限,有可能有的任务可能会被丢弃,由于是回调第三方接口,所以我想把丢弃掉的任务信息记录到日志里,方便后续问题定位。这就需要自定义任务拒绝策略。回想到面试遇到的一些线程池问题,决定整理一下相关信息,所以这篇文章就诞生了。


一、如何构建线程池?


我相信多数用过线程池的Java程序员都用过Executors来创建线程池,该类提供了几个静态方法,可以快速创建线程池。



如上图所示,可以创建四种类型的线程池


  • 固定线程数量的线程池。

  • 根据需要创建线程的线程池。

  • 执行定时任务的线程池。

  • 单个线程的线程池。


多数情况下,这几种类型的线程池就能满足我们的需要。但是实际上还有一个创建线程池的方法那就是手动构造线程池(ThreadPoolExecutor)


二、ThreadPoolExecutor


如果你去看一下前面提到的Executors的几个静态方法的实现,你会发现他们其实就用到了ThreadPoolExecutor,只是根据不同的场景传入了不同的参数。完整的ThreadPoolExecutor总共有七个参数 如图



  • corePoolSize。核心线程数

  • maximumPoolSize。最大线程数

  • keepAliveTime。线程存活时间

  • unit。 存活时间的单位

  • workQueue。 工作队列

  • threadFactory。构造线程池中线程的工厂

  • handler。任务不能被处理时的拒绝策略


三、工作原理(流程)


上面列出的 几个参数,我虽然都给了中文解释,但是如果不结合原理来描述一下他们的具体作用,有些参数我感觉还是不好理解。所以这里就把线程池工作原理和参数一起讲。


  • 提交任务的时候,判断当前线程池中的存活线程数量是否小于corePoolSize

  • 如果小于corePoolSize,则不管是否有线程处于空闲状态,都会新建一个线程。

  • 如果线程数量已经达到corePoolSize,则将任务扔进队列workQueue

  • 随着任务越来越多,队列可能已经满了,则需要看当前线程是否已经达到了maximumPoolSize,如果没有达到,则创建新的线程,并用它执行该任务。

  • 最坏情况,任务实在太多了,队列已经满了,而线程数量已经达到maximumPoolSize,还有新的任务来,说白了,就是已经满负荷了,任然还有任务需要执行,这个时候就会handler来处理该任务了。


整个工作原理就这样了,标红部分尤其重要,面试稍微深入一点,肯定就会问到这个,一定记住,是先将任务扔进队列,队列满了之后才会继续考虑创建线程。至于为什么要这样设计,可以想一想,想不通可以给我发消息。整个原理说下来,还有3个参数没有提到,这里再说明一下他们的作用


  • keepAliveTime。如我们所知,使用线程池的目的就是为了减少线程的创建,因为创建线程本身是比较耗资源的。由于线程本身需要占用资源,有一种情况就是,某个时候线程数量比较多,但是任务没有多少,就会出现有的线程没有活干,所以我们就可以考虑释放掉其资源,但是呢,我们又无法预知未来的任务量,所以我们就准许其空闲一段时间,如果过了这段时间都还是空闲的,那么就会释放掉其资源(就像在公司上班,可能有段时间没活干,老板可能并不会让你走,要是长时间没活干,老板可能就为了节约成本,要裁员了),这个参数就是用指定这段空闲时间的。默认情况下是有超过corePoolSize个线程时,就会用到该值, 但是也可以指定corePoolSize数量之内的线程空闲时是否释放资源(allowCoreThreadTimeout)。(就类似默认情况下,公司肯定只会裁掉非核心员工,但是实在混不走的时候,核心员工可能也会被干掉)


  • unit 这个参数很好理解,就是单位,就是前面keepAliveTime这个我们准许空闲的时间的单位


  • factory .其类型为ThreadFactory,顾名思义,就是一个创建Thread的Factory. 该接口只有一个方法,产生一个Thread。通常情况下,我们都只需要使用默认的factory就可以了,但是为了定位问题方便,我们可以为线程池创建的线程设置一个名字,这样看日志的时候就比较方便了。


四、RejectExecutionHandler


这个拒绝策略有必要拿出来单独说一下,我今天就是实现了该接口,从而满足了业务需要。


为什么我需要自己实现该接口,而采用Executors静态方法时,并没有让传入该参数呢?实际上Jdk本身提供了四种策略,分别是


  • AbortPolicy。会抛出异常

  • CallerRunnerPolicy。在调用execute的方法中执行被拒绝的任务

  • DiscardOldestPolicy。丢掉队列中最老的任务,然后重试

  • DiscardPolicy。直接丢掉该任务


这四种策略是ThredPoolExecutor的内部类,实现都比较简单,有兴趣的可以看一下。我今天的实现方式也很简单,实际上就是在discardPolicy的基础上增加日志记录。


五、其他


前面说了其工作原理,但是看了一下源代码,其实和描述的原理并不完全一致。主要在处理队列大小的时候,主要是对正在运行的线程数量还个判断,不能超过指定的值,当然这个值比较大,我们一般不会达到这个值,至于具体原因我也么去继续深入研究。


其次就是参数设置,可能需要具体业务场景,任务数量,任务执行速度来调整,并没有一个固定的值。只是记得一定要设置队列大小,不然就使用了一个无界队列,可能就是会内存爆掉。


实际使用场景下,还有一些可以优化的地方,比如对不同类型的任务创建不同的线程池, 比如有的线程比较耗时,有的很快,如果放在同一个线程池里面执行,可能导致队列很快就满了,本来该很快执行完的任务却一直得不到执行。


另外还有ThreadPoolExecutor还提供了一些hook方法,如有需要可以使用


  • beforeExecute()  任务执行之前调用

  • afterExecute() 任务执行之后调用


虽然有点标题党,说是深入理解,其实也并不是特别深入,但是基本上这篇文章的内容掌握过后,个人觉得起码90%以上的问题都能对答如流了。还有10%在哪里? 可以去看一下newCachedThreadPool的实现,他使用的队列不一样。还有就是想一想,如果实现线程存活的功能,让自己实现,怎么来做。另外可能就是真的需要去看一下源码,把具体的worker创建运行过程都搞透彻了。


【关于投稿】


如果大家有原创好文投稿,请直接给公号发送留言。


① 留言格式:
【投稿】+《 文章标题》+ 文章链接

② 示例:
【投稿】《不要自称是程序员,我十多年的 IT 职场总结》:http://blog.jobbole.com/94148/

③ 最后请附上您的个人简介哈~



看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

登录查看更多
0

相关内容

【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
打怪升级!2020机器学习工程师技术路线图
专知会员服务
98+阅读 · 2020年6月3日
Python分布式计算,171页pdf,Distributed Computing with Python
专知会员服务
107+阅读 · 2020年5月3日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
【新书】Java企业微服务,Enterprise Java Microservices,272页pdf
【书籍推荐】简洁的Python编程(Clean Python),附274页pdf
专知会员服务
179+阅读 · 2020年1月1日
教程 | PyTorch经验指南:技巧与陷阱
机器之心
15+阅读 · 2018年7月30日
开发、调试计算机视觉代码有哪些技巧?
AI研习社
3+阅读 · 2018年7月9日
浅显易懂的分布式TensorFlow入门教程
专知
7+阅读 · 2018年6月22日
[机器学习] 用KNN识别MNIST手写字符实战
机器学习和数学
4+阅读 · 2018年5月13日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
【干货】深入理解变分自编码器
专知
21+阅读 · 2018年3月22日
【干货】深入理解自编码器(附代码实现)
[深度学习] 我理解的循环神经网络RNN
机器学习和数学
16+阅读 · 2017年12月2日
用TensorFlow开发问答系统
机器学习研究会
7+阅读 · 2017年11月27日
Python NLP入门教程
Python开发者
9+阅读 · 2017年11月19日
Arxiv
101+阅读 · 2020年3月4日
Knowledge Based Machine Reading Comprehension
Arxiv
4+阅读 · 2018年9月12日
Arxiv
3+阅读 · 2018年3月5日
Arxiv
9+阅读 · 2018年1月30日
Arxiv
9+阅读 · 2018年1月4日
Arxiv
27+阅读 · 2017年12月6日
VIP会员
相关VIP内容
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
打怪升级!2020机器学习工程师技术路线图
专知会员服务
98+阅读 · 2020年6月3日
Python分布式计算,171页pdf,Distributed Computing with Python
专知会员服务
107+阅读 · 2020年5月3日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
【新书】Java企业微服务,Enterprise Java Microservices,272页pdf
【书籍推荐】简洁的Python编程(Clean Python),附274页pdf
专知会员服务
179+阅读 · 2020年1月1日
相关资讯
教程 | PyTorch经验指南:技巧与陷阱
机器之心
15+阅读 · 2018年7月30日
开发、调试计算机视觉代码有哪些技巧?
AI研习社
3+阅读 · 2018年7月9日
浅显易懂的分布式TensorFlow入门教程
专知
7+阅读 · 2018年6月22日
[机器学习] 用KNN识别MNIST手写字符实战
机器学习和数学
4+阅读 · 2018年5月13日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
【干货】深入理解变分自编码器
专知
21+阅读 · 2018年3月22日
【干货】深入理解自编码器(附代码实现)
[深度学习] 我理解的循环神经网络RNN
机器学习和数学
16+阅读 · 2017年12月2日
用TensorFlow开发问答系统
机器学习研究会
7+阅读 · 2017年11月27日
Python NLP入门教程
Python开发者
9+阅读 · 2017年11月19日
相关论文
Arxiv
101+阅读 · 2020年3月4日
Knowledge Based Machine Reading Comprehension
Arxiv
4+阅读 · 2018年9月12日
Arxiv
3+阅读 · 2018年3月5日
Arxiv
9+阅读 · 2018年1月30日
Arxiv
9+阅读 · 2018年1月4日
Arxiv
27+阅读 · 2017年12月6日
Top
微信扫码咨询专知VIP会员