Executors快捷创建线程池的潜在问题
写在前面
又是一个金九银十的面试季,相信很多小伙伴都会被问过创建线程有几种方式?有不少的博客都会回答有四种创建方式,分别是以下方式:
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程
4)使用线程池例如用Executor框架
但是看过阿里巴巴开发规范手册的,都知道在手册里面写着 线程池不允许使用Executors去创建。
Executors的4种快捷创建线程池的方法
方法名 | 描述 |
---|---|
newSingleThreadExecutor() | 创建只有一个线程的线程池 |
newFixedThreadPool(int nThreads) | 创建固定大小的线程池 |
newCachedThreadPool() | 创建一个不限制数量的线程池 |
newScheduledThreadPool | 创建一个可定期或延时执行的线程池 |
newSingleThreadExecutor的问题
使用newFixedThreadPool工厂方法创建“固定数量的线程池”的源码如下
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, // 核心线程数nThreads, // 最大线程数0L, // 线程最大空闲(Idle)时长TimeUnit.MILLISECONDS, // 时间单位:毫秒new LinkedBlockingQueue<Runnable>() //任务的排队队列,无界队列);}
newFixedThreadPool工厂方法返回一个ThreadPoolExecutor实例,该线程池实例的corePoolSize数量为参数nThread,其maximumPoolSize数量也为参数nThread,其workQueue属性的值为LinkedBlockingQueue<Runnable>()无界阻塞队列。
使用Executors创建“固定数量的线程池”的潜在问题主要存在于其workQueue上,其值为LinkedBlockingQueue(无界阻塞队列)。如果任务提交速度持续大于任务处理速度,就会造成队列中大量的任务等待。
如果队列很大,很有可能导致JVM出现OOM(Out Of Memory)异常,即内存资源耗尽。
newSingleThreadExecutor的问题
先看源码:
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, // 核心线程数1, // 最大线程数0L, // 线程最大空闲(Idle)时长TimeUnit.MILLISECONDS, //时间单位:毫秒new LinkedBlockingQueue<Runnable>() //无界队列));}
FinalizableDelegatedExecutorService对该“固定大小的线程池”进行包装,这一层包装的作用是防止线程池的corePoolSize被动态地修改。
使用Executors创建的“单线程化线程池”与“固定大小的线程池”一样,其潜在问题仍然存在于其workQueue属性上,该属性的值为LinkedBlockingQueue(无界阻塞队列)。
如果任务提交速度持续大于任务处理速度,就会造成队列大量阻塞。如果队列很大,很有可能导致JVM的OOM异常,甚至造成内存资源耗尽。
newCachedThreadPool的问题
还是先看源码:
public static ExecutorService newCachedThreadPool(){return new ThreadPoolExecutor(0, // 核心线程数Integer.MAX_VALUE, // 最大线程数60L, // 线程最大空闲(Idle)时长TimeUnit.MILLISECONDS, // 时间单位:毫秒new SynchronousQueue<Runnable>() // 任务的排队队列,无界队列);}
以上代码通过调用ThreadPoolExecutor标准构造器创建一个核心线程数为0、最大线程数不设限制的线程池。
所以,理论上“可缓存线程池”可以拥有无数个工作线程,即线程数量几乎无限制。
“可缓存线程池”的workQueue为SynchronousQueue同步队列,这个队列类似于一个接力棒,入队出队必须同时传递,正因为“可缓存线程池”可以无限制地创建线程,不会有任务等待,所以才使用SynchronousQueue。
使用Executors创建的“可缓存线程池”的潜在问题存在于其最大线程数量不设限上。
由于其maximumPoolSize的值为Integer.MAX_VALUE(非常大),可以认为可以无限创建线程,如果任务提交较多,就会造成大量的线程被启动,很有可能造成OOM异常,甚至导致CPU线程资源耗尽。
newScheduledThreadPool的问题
“可调度线程池”的源码如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}
newScheduledThreadPool工厂方法调用了ScheduledThreadPoolExecutor实现类的构造器,而
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor的普通线程池类,在其构造器内部进一步调用了该父类的构造器,具体的代码如下:
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, // 核心线程数Integer.MAX_VALUE, // 最大线程数0, //线程最大空闲(Idle)时长NANOSECONDS,//时间单位new DelayedWorkQueue() //任务的排队队列);}
的值为Integer.MAX_VALUE(非常大),可以认为可以无限创建线程,如果任务提交较多,就会造成大量的线程被启动,很有可能造成OOM异常,甚至导致CPU线程资源耗尽。
4.使用Executors创建“可调度线程池”的潜在问题
“可调度线程池”的源码如下
public static ScheduledExecutorService
newScheduledThreadPool(int corePoolSize){return new
ScheduledThreadPoolExecutor(corePoolSize);}
newScheduledThreadPool工厂方法调用了ScheduledThreadPoolExecutor实现类的构造器,而
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor的普通线程池类,在其构造器内部进一步调用了该父类的构造器,具体的代码如下:
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, // 核心线程数Integer.MAX_VALUE, // 最大线程数0, //线程最大空闲(Idle)时长NANOSECONDS,//时间单位new DelayedWorkQueue() //任务的排队队列);}
使用Executors创建的“可缓存线程池”的潜在问题存在于其最大线程数量不设限上。由于其线程数量不设限,如果到期任务太多,就会导致CPU的线程资源耗尽。
小小的总结
以上内容分别梳理了Executors四个工厂方法所创建的线程池将面临的潜在问题。总结起来,使用Executors创建线程池主要的弊端如下:
方法 | 问题 |
---|---|
FixedThreadPool和SingleThreadPool | 这两个工厂方法所创建的线程池,工作队列(任务排队的队列)的长度都为Integer.MAX_VALUE,可能会堆积大量的任务,从而导致OOM(即耗尽内存资源)。 |
CachedThreadPool和ScheduledThreadPool | 这两个工厂方法所创建的线程池允许创建的线程数量为Integer.MAX_VALUE,可能会导致创建大量的线程,从而导致OOM |
所以,大厂的编程规范都不允许使用Executors创建线程池,而是要求使用标准构造器ThreadPoolExecutor创建线程池。
Executors快捷创建线程池的潜在问题相关推荐
- inputstreamreader未关闭会导致oom_【搞定面试官】你还在用Executors来创建线程池?会有什么问题呢?
前言 上文我们介绍了JDK中的线程池框架Executor.我们知道,只要需要创建线程的情况下,即使是在单线程模式下,我们也要尽量使用Executor.即: ExecutorService fixedT ...
- Executors如何创建线程池?
Executors如何创建线程池? Executors 类是从 JDK 1.5 开始就新增的线程池创建的静态工厂类,它就是创建线程池的,但是很多的大厂已经不建议使用该类去创建线程池.原因在于,该类创建 ...
- 为什么阿里巴巴Java开发手册中不允许用Executors去创建线程池?
在我阅读阿里巴巴开发手册的时候,有一段关于多线程的描述: 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池 ...
- 理解阿里不允许用Executors去创建线程池
一.简述 阿里开发手册关于多线程的一段描述:线程池不允许使用 Executors 去创建,而是通过ThreadPoolExecutor的方式.这样的处理方式让写的同学更加明确线程池的运行规则,规避资源 ...
- 为什么阿里巴巴要禁用 Executors 创建线程池?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 转自:掘金,作者:何甜甜在吗 juejin.im/post/5dc ...
- 为什么阿里巴巴要禁用Executors创建线程池?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:何甜甜在吗 juejin.im/post/5dc41c165 ...
- 阿里内部禁用Executors创建线程池,为什么?
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 何甜甜在吗 来源 | http://rrd.m ...
- 阿里巴巴为什么要禁用 Executors 创建线程池?
作者:何甜甜在吗 www.juejin.im/post/5dc41c165188257bad4d9e69 看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用 Executors 去创建,而是通过T ...
- 阿里为什么禁用Executors创建线程池?
作者 | 何甜甜在吗 来源 | http://rrd.me/eUh6V 看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方 ...
最新文章
- 机器学习、超参数、最优超参数、网格搜索、随机搜索、贝叶斯优化、Google Vizier、Adviser
- 使用Android Studio的时候如何查看开发文档,API文档,doc文档,SDK文档
- SVN 常用命令笔记
- 200905阶段一C++链表与继承特性
- 作者:周园春(1975-),男,中国科学院计算机网络信息中心研究员、博士生导师...
- Qt文档阅读笔记-Q_GADGET官方解析及实例
- JZOJ 6290. 倾斜的线
- 最新版FFmpeg移植Android:编译so库(基于NDK r20和FFmpeg-4.1.0)
- leetcode python3 简单题69. Sqrt(x)
- .net中三种Timer使用总结
- layUI数据表格可编辑扩展下拉框
- java控制语句(超详细!)
- 全局空间自相关算法:Join Count
- 扩展欧几里得算法的实现
- 串口485接法图_rs485通讯接口定义图详解
- php 事件流转,php46公文流转
- Android 中指纹识别的使用
- 从Mac远程控制Windows
- http header
- 服务器绑定自己的域名-腾讯云
热门文章
- Python使用列表完成程序的编写:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到m报数),凡是报到m的人退出圈子,问最后留下的是原来第几号的人。
- 如何做研究(How to research)
- VDP部署图解及注册失败处理
- 使用RANK函数按计算机基础的降序求名次,使用Excel中Rank函数对学生成绩名次进行排列...
- nprogress官网教程小记
- GROUP BY和having联合使用相关问题
- 黑龙江大学的计算机科学技术专业的,黑龙江大学计算机科学技术学院师资队伍...
- python rgb转lab_RGB转LAB色彩空间
- 华夏银行招聘计算机笔试题,2019华夏银行招聘计算机模拟习题及答案
- 118_IMtoken中,代币名字后面多了数字