启用Executor初始化线程池
前言
上文我们介绍了JDK中的线程池框架Executor
。我们知道,只要需要创建线程的情况下,即使是在单线程模式下,我们也要尽量使用Executor
。即:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1);
//此处不该利用Executors工具类来初始化线程池
但是,在《阿里巴巴Java开发手册》中有一条
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors 返回的线程池对象的弊端如下:
FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
可以看到,这是一个强制性的规则,并且是不允许使用Executors
来创建,建议使用ThreadPoolExecutor
来创建线程池,那我们先来回顾一下Executors
和ThreadPoolExecutor
。
我们可以看到ThreadPoolExecutor
已经是Executor
的具体实现了,而且具有较多可配参数(可配参数见下方,可仅了解,用到时再进行详细查询)。Executors
是一个创建线程池的工具类,查看其源码的话也会发现这几种创建线程池的方法也都是通过调用ThreadPoolExecutor来实现的。
ThreadPoolExecutor一共有四个构造函数,七个可配参数,分别是
- corePoolSize: 线程池中保持存活线程的数量。
- maximumPoolSize: 线程池中允许线程数量的最大值
- keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止
- unit: 参数keepAliveTime的时间单位
- workQueue: 一个阻塞队列,用来存储等待执行的任务
- threadFactory: 线程工厂,主要用来创建线程
- handler:表示当拒绝处理任务时的策略
分析
那么Executors到底会导致什么问题,才会让开发手册中直接被定义为不允许了呢。首先就是一个血淋淋的教训,直接导致线上服务不可用,已经可以算是事故了。
实验
我们也可以现在我们本地进行一下小实验:
public class ExecutorsTesting {
private static ExecutorService executor = Executors.newFixedThreadPool(15);
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executor.execute(new SubThread());
}
}
}
class SubThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
//do nothing
}
}
}
运行时指定JVM参数:-Xmx8m -Xms8m
,大概几秒钟之后,会报出OOM错误:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.kaikeba.mybatis.ExecutorsTesting.main(ExecutorsTesting.java:10)
//报错行数为上述代码中的executor.execute(new SubThread());
那么为什么会报出这个错误呢。
源码分析
我们先来看一下Executors
中的FixedThreadPool
是如何构造的。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看到对于存储等待执行的任务,FixedThreadPool
是通过LinkedBlockingQueue
来实现的。而我们知道LinkedBlockingQueue
是一个链表实现的阻塞队列,而如果不设置其容量的话,将会是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE
。由于Executors
中并未设置容量,所以应用可以不断向队列中添加任务,导致OOM错误。
上面提到的问题主要体现在newFixedThreadPool
和newSingleThreadExecutor
两个工厂方法上,并不是说newCachedThreadPool
和newScheduledThreadPool
这两个方法就安全了,这两种方式创建的最大线程数可能是Integer.MAX_VALUE
,而创建这么多线程,必然就有可能导致OOM。
如何该利用ThreadPoolExecutor来创建线程池呢?
我们其实可以看到Executors
中的newFixedThreadPool
其实也是调用ThreadPoolExecutor
来实现的。正如手册中所说,当我们不用Executors
默认创建线程池的方法,而直接自己手动去调用ThreadPoolExecutor
,可以让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。比如我们在Executors.newFixedThreadPool
基础上给LinkedBlockingQueue
加一个容量,当队列已经满了,而仍需要添加新的请求会抛出相应异常,我们可以根据异常做相应处理。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(10)); //添加容量大小
}
除了自己定义ThreadPoolExecutor
外。还可以利用其它开源类库,如apache和guava等,可以有更多个性化配置。
启用Executor初始化线程池的更多相关文章
- 多线程-Executors和Executor,线程池
jdk1.5之前,所有的线程都是需要自己手动创建的,由jvm销毁,当请求过多的时候,频繁的创建和销毁线程是非常浪费资源的.jdk1.5为此做了优化,提供了 java.util.concurrent 包 ...
- 由初始化线程池引发的NoClassDefFoundError 异常分析
今天说的异常是一个很不常见的异常,至少我不经常见到这个异常.首先先看下NoClassDefFoundError官方定义 : Java Virtual Machine is not able to fi ...
- Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)
最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...
- JAVA Executor(线程池)框架
一.Executor概述 为更好控制线程,jdk提供一套线程管理框架Executor,帮助开发人员有效地进行线程控制.它们都位于java.util.concurrent包中,是jdk并发包的核心.其中 ...
- 戏(细)说Executor框架线程池任务执行全过程(上)
一.前言 1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦.要执行任务的人只需把Task描述清楚,然后提交即可.这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不 ...
- 戏(细)说Executor框架线程池任务执行全过程(下)
上一篇文章中通过引入的一个例子介绍了在Executor框架下,提交一个任务的过程,这个过程就像我们老大的老大要找个老大来执行一个任务那样简单.并通过剖析ExecutorService的一种经典实现Th ...
- Executor以及线程池
在应用程序中,总是会出现大量的任务,包括相同类型的和不同类型的.要快速处理这些任务,常见方法就是利用多线程,但是也不可能为每个任务都创建一个线程,这样内存也不够,并且线程的创建销毁开销很大.最好是少量 ...
- Java 并发 —— Thread、Executor、线程池
Java 线程池: ThreadPoolExecutor,创建此线程池的方法: new Executors.newCachedThreadPool():尽量避免使用,其无法控制线程数量, Schedu ...
- Java并发编程的艺术(十一)——Executor与线程池
Executor框架简介 从JDK5开始,把工作单元和执行机制分离开来了,工作的单元包括Runnable和Callable,执行机制就是由Executor框架提供. Executor两级调度模型 Ho ...
随机推荐
- java实现整数计算器
计算器代码 package stack; import java.util.ArrayList; import java.util.List; import java.util.Scanner; im ...
- HDU-4219-Randomization?
题目描述 给定一棵\(n\)个节点的树,每条边的权值为\([0,L]\)之间的随机整数,求这棵树两点之间最长距离不超过\(S\)的概率. Input 第一行三个整数\(n,L,S\) 接下来n-1行, ...
- 初识JavaScript(三)
初识JavaScript(三) 我从上一讲<初识JavaScript(二)>了解到了类型.值.变量的定义以及特点,本节我将学习到JavaScript中的算术运算.二进制浮点数和四舍五入的错 ...
- tarjan算法求无向图的桥、边双连通分量并缩点
// tarjan算法求无向图的桥.边双连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> ...
- ds replicas是什么
以上是我百度的答案.很显然,这个对于初学者很不友好,中文的每一个靠谱的,英文的都TM是how 和 why .But 我要 what! 所以只能自己搞,自己琢磨,搞完分享出来,助力后来者不掉坑,有所查. ...
- Day2_数字类型_字符串类型_列表类型
数字类型: 作用:年纪,等级,薪资,身份证号等: 10进制转为2进制,利用bin来执行. 10进制转为8进制,利用oct来执行. 10进制转为16进制,利用hex来执行. #整型age=10 prin ...
- 剑指offer 数字翻译成字符串
0 -> 'a', 1->'b', ..., 11 -> 'l', ..., 25->'z'. 计算一个数有多少种不同的翻译方法. 分析:记f[i]表示从第i位起的不同翻译数目 ...
- wex5 如何写后台BAAS
Data.java: 在class中链接数据源: 配置的numsql数据源 private static final String DATASOURCE_NUMYSQL = "numysql ...
- Java Blob类型和String类型相互转换
1.String 转 Blob: String content = "Hello World!"; Blob blob = Hibernate.createBlob(content ...
- 字符串的 Base64 加密和解密
base64加密: FORM encode_base64 USING p_business “字符串string CHANGING p_base64. DATA l_xstring TYPE xstr ...