写在前面

又是一个金九银十的面试季,相信很多小伙伴都会被问过创建线程有几种方式?有不少的博客都会回答有四种创建方式,分别是以下方式:
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快捷创建线程池的潜在问题相关推荐

  1. inputstreamreader未关闭会导致oom_【搞定面试官】你还在用Executors来创建线程池?会有什么问题呢?

    前言 上文我们介绍了JDK中的线程池框架Executor.我们知道,只要需要创建线程的情况下,即使是在单线程模式下,我们也要尽量使用Executor.即: ExecutorService fixedT ...

  2. Executors如何创建线程池?

    Executors如何创建线程池? Executors 类是从 JDK 1.5 开始就新增的线程池创建的静态工厂类,它就是创建线程池的,但是很多的大厂已经不建议使用该类去创建线程池.原因在于,该类创建 ...

  3. 为什么阿里巴巴Java开发手册中不允许用Executors去创建线程池?

    在我阅读阿里巴巴开发手册的时候,有一段关于多线程的描述: 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池 ...

  4. 理解阿里不允许用Executors去创建线程池

    一.简述 阿里开发手册关于多线程的一段描述:线程池不允许使用 Executors 去创建,而是通过ThreadPoolExecutor的方式.这样的处理方式让写的同学更加明确线程池的运行规则,规避资源 ...

  5. 为什么阿里巴巴要禁用 Executors 创建线程池?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 转自:掘金,作者:何甜甜在吗 juejin.im/post/5dc ...

  6. 为什么阿里巴巴要禁用Executors创建线程池?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:何甜甜在吗 juejin.im/post/5dc41c165 ...

  7. 阿里内部禁用Executors创建线程池,为什么?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 何甜甜在吗 来源 | http://rrd.m ...

  8. 阿里巴巴为什么要禁用 Executors 创建线程池?

    作者:何甜甜在吗 www.juejin.im/post/5dc41c165188257bad4d9e69 看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用 Executors 去创建,而是通过T ...

  9. 阿里为什么禁用Executors创建线程池?

    作者 | 何甜甜在吗 来源 | http://rrd.me/eUh6V 看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方 ...

最新文章

  1. 机器学习、超参数、最优超参数、网格搜索、随机搜索、贝叶斯优化、Google Vizier、Adviser
  2. 使用Android Studio的时候如何查看开发文档,API文档,doc文档,SDK文档
  3. SVN 常用命令笔记
  4. 200905阶段一C++链表与继承特性
  5. 作者:周园春(1975-),男,中国科学院计算机网络信息中心研究员、博士生导师...
  6. Qt文档阅读笔记-Q_GADGET官方解析及实例
  7. JZOJ 6290. 倾斜的线
  8. 最新版FFmpeg移植Android:编译so库(基于NDK r20和FFmpeg-4.1.0)
  9. leetcode python3 简单题69. Sqrt(x)
  10. .net中三种Timer使用总结
  11. layUI数据表格可编辑扩展下拉框
  12. java控制语句(超详细!)
  13. 全局空间自相关算法:Join Count
  14. 扩展欧几里得算法的实现
  15. 串口485接法图_rs485通讯接口定义图详解
  16. php 事件流转,php46公文流转
  17. Android 中指纹识别的使用
  18. 从Mac远程控制Windows
  19. http header
  20. 服务器绑定自己的域名-腾讯云

热门文章

  1. Python使用列表完成程序的编写:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到m报数),凡是报到m的人退出圈子,问最后留下的是原来第几号的人。
  2. 如何做研究(How to research)
  3. VDP部署图解及注册失败处理
  4. 使用RANK函数按计算机基础的降序求名次,使用Excel中Rank函数对学生成绩名次进行排列...
  5. nprogress官网教程小记
  6. GROUP BY和having联合使用相关问题
  7. 黑龙江大学的计算机科学技术专业的,黑龙江大学计算机科学技术学院师资队伍...
  8. python rgb转lab_RGB转LAB色彩空间
  9. 华夏银行招聘计算机笔试题,2019华夏银行招聘计算机模拟习题及答案
  10. 118_IMtoken中,代币名字后面多了数字