前言

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

  1. ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
  2. //此处不该利用Executors工具类来初始化线程池

但是,在《阿里巴巴Java开发手册》中有一条

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors 返回的线程池对象的弊端如下:

FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

可以看到,这是一个强制性的规则,并且是不允许使用Executors来创建,建议使用ThreadPoolExecutor来创建线程池,那我们先来回顾一下ExecutorsThreadPoolExecutor

我们可以看到ThreadPoolExecutor已经是Executor的具体实现了,而且具有较多可配参数(可配参数见下方,可仅了解,用到时再进行详细查询)。Executors是一个创建线程池的工具类,查看其源码的话也会发现这几种创建线程池的方法也都是通过调用ThreadPoolExecutor来实现的。

ThreadPoolExecutor一共有四个构造函数,七个可配参数,分别是

  1. corePoolSize: 线程池中保持存活线程的数量。
  2. maximumPoolSize: 线程池中允许线程数量的最大值
  3. keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止
  4. unit: 参数keepAliveTime的时间单位
  5. workQueue: 一个阻塞队列,用来存储等待执行的任务
  6. threadFactory: 线程工厂,主要用来创建线程
  7. handler:表示当拒绝处理任务时的策略

分析

那么Executors到底会导致什么问题,才会让开发手册中直接被定义为不允许了呢。首先就是一个血淋淋的教训,直接导致线上服务不可用,已经可以算是事故了。

实验

我们也可以现在我们本地进行一下小实验:

  1. public class ExecutorsTesting {
  2. private static ExecutorService executor = Executors.newFixedThreadPool(15);
  3. public static void main(String[] args) {
  4. for (int i = 0; i < Integer.MAX_VALUE; i++) {
  5. executor.execute(new SubThread());
  6. }
  7. }
  8. }
  9. class SubThread implements Runnable {
  10. @Override
  11. public void run() {
  12. try {
  13. Thread.sleep(10000);
  14. } catch (InterruptedException e) {
  15. //do nothing
  16. }
  17. }
  18. }

运行时指定JVM参数:-Xmx8m -Xms8m,大概几秒钟之后,会报出OOM错误:

  1. Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
  2. at com.kaikeba.mybatis.ExecutorsTesting.main(ExecutorsTesting.java:10)
  3. //报错行数为上述代码中的executor.execute(new SubThread());

那么为什么会报出这个错误呢。

源码分析

我们先来看一下Executors中的FixedThreadPool是如何构造的。

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }

可以看到对于存储等待执行的任务,FixedThreadPool是通过LinkedBlockingQueue来实现的。而我们知道LinkedBlockingQueue是一个链表实现的阻塞队列,而如果不设置其容量的话,将会是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE由于Executors中并未设置容量,所以应用可以不断向队列中添加任务,导致OOM错误

上面提到的问题主要体现在newFixedThreadPoolnewSingleThreadExecutor两个工厂方法上,并不是说newCachedThreadPoolnewScheduledThreadPool这两个方法就安全了,这两种方式创建的最大线程数可能是Integer.MAX_VALUE,而创建这么多线程,必然就有可能导致OOM。

如何该利用ThreadPoolExecutor来创建线程池呢?

我们其实可以看到Executors中的newFixedThreadPool其实也是调用ThreadPoolExecutor来实现的。正如手册中所说,当我们不用Executors默认创建线程池的方法,而直接自己手动去调用ThreadPoolExecutor,可以让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。比如我们在Executors.newFixedThreadPool基础上给LinkedBlockingQueue加一个容量,当队列已经满了,而仍需要添加新的请求会抛出相应异常,我们可以根据异常做相应处理。

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>(10)); //添加容量大小
  5. }

除了自己定义ThreadPoolExecutor外。还可以利用其它开源类库,如apache和guava等,可以有更多个性化配置。

参考文章:

https://www.hollischuang.com/archives/2888

https://stackoverflow.com/questions/1094867/when-should-we-use-javas-thread-over-executor#answer-34373289

https://blog.51cto.com/zero01/2306857


本文由博客一文多发平台 OpenWrite 发布!

文章首发:https://zhuanlan.zhihu.com/lovebell

个人公众号:技术Go

您的点赞与支持是作者持续更新的最大动力!

【搞定面试官】你还在用Executors来创建线程池?会有什么问题呢?的更多相关文章

  1. 【搞定面试官】谈谈你对JDK中Executor的理解?

    ## 前言 随着当今处理器计算能力愈发强大,可用的核心数量越来越多,各个应用对其实现更高吞吐量的需求的不断增长,多线程 API 变得非常流行.在此背景下,Java自JDK1.5 提供了自己的多线程框架 ...

  2. 搞定面试官 - MySQL 中你知道如何计算一个索引的长度嘛?

    大家好,我是程序员啊粥. 今天给大家分享一个我遇到过的比较少见的面试题,那就是 MySQL 中如何计算一个索引的长度. 说实话,我第一次遇到这个问题的时候想当然的以为索引长度就是我们建表时定义的字段长 ...

  3. 搞定面试官 - 你可以介绍一下在 MySQL 中,哪些情况下 索引会失效嘛?

    大家好,我是程序员啊粥,前边给大家分享了 *MySQL InnoDB 索引模型 在 MySQL InnoDB 中,为什么 delete 删除数据之后表数据文件大小没有变 如何计算一个索引的长度 如何查 ...

  4. 【搞定面试官】try中有return,finally还会执行吗?

    本篇文章我们主要探讨 一下如果try {}语句中有return,这种情况下finally语句还会执行吗?其实JVM规范是对这种情况有特殊规定的,那我就先上代码吧! public class Final ...

  5. 金三银四,2018最新iOS面试题,由它可以搞定面试官?

    序言 这些资料,你一定会用到!我相信很多人都在说,iOS行业不好了,iOS现在行情越来越难了,失业的人比找工作的人还要多.失业即相当于转行,跳槽即相当于降低自己的身价.那么做iOS开发的你,你是否在时 ...

  6. 【搞定面试官】- Synchronized如何实现同步?锁优化?(1)

    前言 说起Java面试中最高频的知识点非多线程莫属.每每提起多线程都绕不过一个Java关键字--synchronized.我们都知道该关键字可以保证在同一时刻,只有一个线程可以执行某个方法或者某个代码 ...

  7. 搞定面试官:咱们从头到尾再说一次 Java 垃圾回收

    接着前几天的两篇文章,继续解析JVM面试问题,送给年后想要跳槽的小伙伴 万万没想到,面试中,连 ClassLoader类加载器 也能问出这么多问题..... 万万没想到,JVM内存区域的面试题也可以问 ...

  8. RabbitMQ:从入门到搞定面试官

    安装 使用docker安装,注意要安装tag后缀为management的镜像(包含web管理插件),我这里使用的是rabbitmq:3.8-management 1. 拉取镜像 shell docke ...

  9. 搞定面试官 - 可以介绍一下在 MySQL 中你平时是怎么使用 COUNT() 的嘛?

    大家好,我是程序员啊粥. 相信在大家的工作中,有很多的功能都需要用到 count(*) 来统计表中的数据行数.同时,对于一些大数据的表,用 count 都是瑟瑟发抖,往往会结合缓存等进行处理. 那么, ...

随机推荐

  1. SpringBoot与MybatisPlus整合之SQL分析插件(六)

    pom.xml: <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifac ...

  2. redis is configured to save RDB snapshots

    Redis被配置为保存数据库快照,但它目前不能持久化到硬盘.用来修改集合数据的命令不能用 原因: 强制关闭Redis 快照导致不能持久化. 解决方法: 运行 config set stop-write ...

  3. 数据文件包解析工具类 RandomAccessFile

    public class ReadTextFile { public static void main(String[] args) { pic2txt(); parseFrmFile(); //ur ...

  4. 用GitLab Runner自动部署GitBook并不难

    相信很多程序员喜欢用 GitBook 来写电子书.教程或者博客,看了不少文章,貌似都缺少说明如何将 GitBook 部署到版本库,并自动在服务器上 build,然后将生成的静态网站部署到云服务器上. ...

  5. Mysql 索引类型+索引方法

    MYSQL索引: PRIMARY(唯一且不能为空:一张表只能有一个主键索引). INDEX(普通索引). UNIQUE(唯一性索引). FULLTEXT(全文索引:用于搜索很长一篇文章的时候,效果最好 ...

  6. 【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之优雅增删改

    [前言] 大家好,我是TANZAME.出乎意料的,我们在立冬的前一天又见面了,天气慢慢转凉,朋友们注意添衣保暖,愉快撸码.距离 TZM.XFramework 的首秀已数月有余,期间收到不少朋友的鼓励. ...

  7. CSPS模拟 42

    T3数位$dp$还没改完啊 哭了 T1 对$DAG$里所有点求他能到达的点的数量 考试时算了$bitset$内存扛不住,yy了个线段树合并上去 没有A不是因为被卡了,而是数组又开小了.. 我以为不能卡 ...

  8. hadoop2.6集群环境搭建

    版权声明:本文为博主原创文章,未经博主允许不得转载. 一.环境说明 1.机器:一台物理机 和一台虚拟机 2.Linux版本:[Spark@S1PA11 ~]$ cat /etc/issueRed Ha ...

  9. svn:"Update item to this version"、"Revert to this version"及"Revert changes from this version"详细说明

    背景: 今天在svn分支上做了一些课题性研究,发现当前的环境版本不稳定, 和领导研究后决定还原到前面的版本以继续进行课题. 问题: 因此遇到了问题,是应该选择“Update item to this ...

  10. NuGet Package Explorer使用教程下载

    1.下载NuGet Package Explorer http://www.pc0359.cn/downinfo/91514.html 2.双击NuGet Package Explorer出现启动界面 ...