线程池初始化严禁使用Executors

简介: 阿里巴巴规范禁止使用Executors创建线程池,因可能引发OOM。如newFixedThreadPool使用无界队列,任务堆积易导致内存溢出;newCachedThreadPool可创建过多线程,同样存在OOM风险。推荐通过ThreadPoolExecutor或Guava方式显式设置线程数、队列容量等,避免资源耗尽,提升系统稳定性与可追溯性。

使用线程池时候,我们可能会使用下面四个场景,这在alibaba代码规范中都是明令禁止的

// 创建一个单线程化的Executor[因为数量固定,可能会堆积大量请求,导致OOM]
private static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

// 创建一个固定数目线程的线程池[因为数量固定,可能会堆积大量请求,导致OOM]
private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);

// 创建一个可执行命令的单线程Executor[可能会创建大量的线程,导致OOM]
private static ExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();

// 创建一个可缓存的线程池(60S存活时间)[可能会创建大量的线程,导致OOM]
private static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

我们先来一个简单的例子,模拟一下使用 Executors 导致 OOM 的情况。
public class ExecutorsDemo {
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 java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)
以上代码指出,ExecutorsDemo.java 的第 16 行,就是代码中的 executor.execute(new SubThread());。
通过上面的例子,我们知道了 Executors 创建的线程池存在 OOM 的风险,那么到底是什么原因导致的呢?我们需要深入 Executors 的源码来分析一下。其实,在上面的报错信息中,我们是可以看出蛛丝马迹的,在以上的代码中其实已经说了,真正的导致 OOM 的其实是 LinkedBlockingQueue.offer 方法。
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at com.hollis.ExecutorsDemo.main(ExecutorsDemo.java:16)

如果读者翻看代码的话,也可以发现,其实底层确实是通过 LinkedBlockingQueue 实现的:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}

如果读者对 Java 中的阻塞队列有所了解的话,看到这里或许就能够明白原因了。Java 中 的 BlockingQueue 主 要 有 两 种 实 现, 分 别 是 ArrayBlockingQueue 和 LinkedBlockingQueue。ArrayBlockingQueue 是一个用数组实现的有界阻塞队列,必须设置容量。LinkedBlockingQueue 是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为 Integer.MAX_VALUE。这里的问题就出在:不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。也就是说,如果我们不设置 LinkedBlockingQueue 的容量的话,其默认容量将会是 Integer.MAX_VALUE。 而 newFixedThreadPool 中创建 LinkedBlockingQueue 时,并未指定容量。此时,LinkedBlockingQueue 就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。上面提到的问题主要体现在 newFixedThreadPool 和 newSingleThreadExecutor 两个工厂方法上,并不是说newCachedThreadPool 和 newScheduledThreadPool 这两个方法就安全了,这两种方式创建的最大线程数可能是Integer.MAX_VALUE,而创建这么多线程,必然就有可能导致 OOM

正确使用:
private static ExecutorService executor = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(10));
这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出java.util.concurrent.RejectedExecutionException,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是异常(Exception)总比发生错误(Error)要好。
但是部分alibaba作者更推荐使用guava创建对应的线程池,示例如下:
public class ExecutorsDemo {
private static ThreadFactory namedThreadFactory = new
ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(1024), namedThreadFactory, new
ThreadPoolExecutor.
AbortPolicy());
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
pool.execute(new SubThread());
}
}
}
通过上述方式创建线程时,不仅可以避免 OOM 的问题,还可以自定义线程名称,更加方便的出错的时候溯源。

相关文章
|
Java 数据安全/隐私保护 Android开发
如何查看签名后的jks文件信息(查看应用签名)
如何查看签名后的jks文件信息(查看应用签名)
5706 0
如何查看签名后的jks文件信息(查看应用签名)
|
10月前
|
人工智能 监控 安全
LLM提示注入攻击深度解析:从原理到防御的完整应对方案
本文深入解析提示注入——OWASP LLM Top 10头号风险,揭示其与传统注入的本质区别及为何无法靠过滤器根治。涵盖直接与间接攻击手法、真实案例,并提出五层纵深防御策略,助你构建弹性AI系统,有效管控风险。
1525 6
LLM提示注入攻击深度解析:从原理到防御的完整应对方案
|
4月前
|
弹性计算 人工智能 测试技术
2026年阿里云便宜云服务器推荐与选购指南
2026年阿里云推出史上最强优惠:打破新老用户壁垒,实现“新老同价、续费同价”。99元/年e实例、199元/年u1实例长期稳定;新用户可抢38元/年轻量服务器;企业享百万迁云补贴与GPU 4折。省钱避坑指南,助你轻松上云!
828 4
|
人工智能 JSON 边缘计算
从零开始学MCP(1)| MCP 协议核心原理解析
MCP 协议统一 AI 工具调用标准,解决碎片化、高耦合与上下文丢失问题,采用 Client/Server 架构,支持上下文传递与 SSE 流式响应,提升工具调用效率与灵活性。
|
11月前
|
安全 Java 编译器
从MESA模型到锁升级:synchronized性能逆袭的底层逻辑
管程(Monitor)是管理共享资源并发访问的核心机制,基于MESA模型实现,通过入口队列和条件变量队列解决互斥与同步问题。Java的synchronized关键字借鉴该模型,利用monitorenter/monitorexit指令或ACC_SYNCHRONIZED标志实现对象锁,配合wait/notify实现线程通信。JDK 6后引入偏向锁、轻量级锁、自旋锁等优化,显著提升性能。使用时需注意死锁防范、锁粒度控制及可重入特性,合理应用可保障线程安全并提升并发效率。(238字)
350 6
|
存储 弹性计算 安全
阿里云服务器38元、99元、199元配置、适用场景区别及选择参考
目前,阿里云有多款特价云服务器产品,轻量云服务器2核2G200M峰值带宽38元一年,经济型e实例云服务器2核2G3M带宽99元1年、4核16G10M云服务器70元1个月、210元3个月,8核32G10M带宽160元1个月、480元3个月,通用算力型u1实例2核4G5M带宽199元一年、4核8G云服务器955元一年。本文将详细介绍阿里云的三款特价云服务器产品:38元的轻量应用服务器、99元的云服务器ECS经济型e实例,以及199元的云服务器ECS u1实例,帮助用户更好地了解这些产品的规格、配置、适用场景及购买资格和注意事项。
|
消息中间件 Java Spring
SpringBoot实现RabbitMQ的定向交换机(SpringAMQP 实现Direct定向交换机)
SpringBoot实现RabbitMQ的定向交换机(SpringAMQP 实现Direct定向交换机)
438 1
|
前端开发
[巨详细]安装HBuilder-X教程
【6月更文挑战第6天】下载HBuilder-X 官方网址:https://www.dcloud.io/ 点击网址 ,打开官网
3104 1
|
存储 监控 Java
(十一)彻悟并发之JUC分治思想产物-ForkJoin分支合并框架原理剖析上篇
在上篇文章《深入理解并发之Java线程池、工作原理、复用原理及源码分析》中,曾详细谈到了Java的线程池框架。在其中也说到了JDK提供的四种原生线程池以及自定义线程池,而本文则再来详细谈谈JDK1.7中新推出的线程池:ForkJoinPool。
433 1
|
Arthas 监控 Java
JVM内存问题之使用gperftools分析JNI Memory泄漏的具体步骤是什么
JVM内存问题之使用gperftools分析JNI Memory泄漏的具体步骤是什么
596 2

热门文章

最新文章