为什么阿里禁止使用Executors去创建线程池

 更新时间:2025年12月09日 09:55:11   作者:rchmin  
文章主要介绍了阿里巴巴禁止使用Executors.newFixedThreadPool()方法创建线程池的原因,指出了该方法的无界工作队列带来的隐患,感兴趣的朋友跟随小编一起看看吧

在阿里巴巴的《Java开发手册》中有明确规定:

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

阿里禁止使用Executors并不是说这个类的方法本身有致命的Bug,而是因为它隐藏了资源耗尽的风险,不符合大厂对于系统稳定性和可观测性的苛刻要求。

下面我们以newFixedThreadPool方法为例详细拆解一下它的缺陷以及阿里的考量。

一、Executors.newFixedThreadPool()的底层原理与核心缺陷

首先,我们来看一下这个方法的源码:

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

从源码可以看到,它创建了一个线程池,其核心参数是:

  • 核心线程数 = 最大线程数nThreads
  • 空闲线程存活时间:0(因为核心线程数和最大线程数相等,这个参数失效)
  • 工作队列new LinkedBlockingQueue<Runnable>() 【这是问题的核心】

核心缺陷:无界的工作队列

LinkedBlockingQueue 在默认情况下(无参构造)是一个 无界队列。这意味着它的容量是 Integer.MAX_VALUE,理论上可以无限地向其中添加任务。

这会导致一个严重的问题:当任务提交的速度持续超过线程处理的速度时,任务会在无界队列中无限堆积

这个“无限堆积”会引发一系列连锁反应:

1、内存耗尽(OOM)

  • 每个排队等待执行的任务都会占用一定的内存(例如,任务对象本身、捕获的上下文变量等)。
  • 如果任务产生速度极快(如突发流量、循环中提交任务),而处理速度很慢(如依赖的外部服务响应慢),队列中的任务会越来越多,最终占满整个堆内存,导致 OutOfMemoryError: Java heap space。这会直接导致整个应用崩溃,而不是仅仅线程池不可用。

2、延迟和响应缓慢

  • 即使没有达到OOM,任务在队列中排队的时间也会非常长。一个任务可能被提交后很久才得到执行,导致系统整体响应时间变得不可接受。

3、问题定位困难

  • 由于队列是无界的,系统在OOM之前可能看起来“正常”运行,只是有点慢。当OOM发生时,你只能看到一个内存溢出的错误,但很难快速定位到是哪个线程池、为什么积累了这么多任务。缺乏必要的监控和预警。

二、 为什么阿里等大厂明确禁止使用?

禁止的原因正是基于上述缺陷,并上升到工程规范和架构层面:

1、规避风险,保证系统稳定性

  • 大厂的应用通常是高并发、海量数据的场景,对稳定性要求极高。任何可能导致服务雪崩或OOM的风险都必须从源头规避。使用有界队列是构建“韧性系统”的基本要求。

2、提倡资源管理的显式化

  • Executors 提供的工厂方法像是一个“黑盒”,它隐藏了 ThreadPoolExecutor 的关键参数(如队列类型和大小)。通过强制使用 ThreadPoolExecutor 构造函数,开发者必须显式地设置核心线程数、最大线程数、队列容量、拒绝策略等。这个过程迫使开发者去思考线程池的配置是否合理,加深对线程池运行机制的理解。

3、需要合理的拒绝策略

  • 当使用有界队列时,队列满了之后,就需要拒绝策略来处理新提交的任务。ThreadPoolExecutor 提供了几种内置策略,如:
    • AbortPolicy(默认):抛出 RejectedExecutionException,让调用者感知到异常。
    • CallerRunsPolicy:让提交任务的线程自己去执行它,这相当于一种负反馈,能有效平缓任务提交速率。
    • DiscardPolicy / DiscardOldestPolicy:丢弃任务。
  • 通过选择合适的拒绝策略,可以在系统过载时提供一种“优雅降级”的方案,而不是像无界队列那样默默地吃掉所有内存直至崩溃。

三、 正确的使用姿势

应该直接使用 ThreadPoolExecutor 来创建线程池,并为其设置一个有界的工作队列

import java.util.concurrent.*;
public class SafeThreadPoolExample {
    public static void main(String[] args) {
        // 核心参数
        int corePoolSize = 10;
        int maximumPoolSize = 20;
        long keepAliveTime = 60L;
        int queueCapacity = 1000; // 关键:设置队列容量
        // 手动创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(queueCapacity), // 有界队列
                new ThreadFactory() { // 可选:自定义线程工厂,便于命名和监控
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread t = new Thread(r, "MyApp-Thread-" + r.hashCode());
                        t.setDaemon(false);
                        return t;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy() // 关键:设置合适的拒绝策略
        );
        // 使用 executor 提交任务...
        // executor.execute(() -> { ... });
        // 优雅关闭
        // executor.shutdown();
    }
}

总结

特性/方式Executors.newFixedThreadPool()手动 ThreadPoolExecutor
队列类型无界 (LinkedBlockingQueue)有界 (可配置,如 new LinkedBlockingQueue<>(capacity))
资源风险,可能导致内存溢出(OOM),通过容量控制和管理
拒绝策略初期无效,OOM前相当于无拒绝有效,队列满后触发,可预测系统行为
可观测性差,队列长度无上限,监控困难好,队列有界,便于监控队列堆积情况
阿里规范禁止推荐

结论:Executors.newFixedThreadPool() 的主要缺陷在于其无界工作队列带来的内存溢出风险。阿里等大厂禁止使用主要是为了规避上述风险,同时培养开发者的资源边界意识和风险意识,强制通过 ThreadPoolExecutor 的显式配置来构建更健壮、更可控、更易于监控的线程池,从而保障大规模分布式系统的稳定性。

到此这篇关于为什么阿里禁止使用Executors去创建线程池的文章就介绍到这了,更多相关阿里禁止使用Executors创建线程池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java 中序列化NotSerializableException问题解决办法

    java 中序列化NotSerializableException问题解决办法

    这篇文章主要介绍了java 中序列化NotSerializableException问题解决办法的相关资料,这里对序列化问题进行描述说明,并提供解决办法,希望能帮助到大家,需要的朋友可以参考下
    2017-08-08
  • Java语言一元运算符实例解析

    Java语言一元运算符实例解析

    这篇文章主要介绍了Java语言中的一元运算符实例解析,需要的朋友可以参考下。
    2017-09-09
  • Spring监听器及定时任务实现方法详解

    Spring监听器及定时任务实现方法详解

    这篇文章主要介绍了Spring监听器及定时任务实现方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • Java Swing实现自定义窗口CusFrame

    Java Swing实现自定义窗口CusFrame

    文章介绍了自定义Swing窗口CusFrame的核心功能,包括移除系统默认标题栏、实现圆角矩形形状、支持拖拽移动和调整大小、多屏幕适配以及窗口状态管理等功能,最后文章提供了使用示例和注意事项,有需要的小伙伴可以参考下
    2026-05-05
  • 从application.properties配置文件获取的汉字乱码的解决方法

    从application.properties配置文件获取的汉字乱码的解决方法

    平时从配置文件各种读取配置参数都正常,但是有时候放了个中文就乱码,你肯定试过网上好多方法,都没解决,那么来看下面,恭喜你终于找这里了,本文给大家介绍了从application.properties配置文件获取的汉字乱码的解决方法,需要的朋友可以参考下
    2024-03-03
  • hibernate中的对象关系映射

    hibernate中的对象关系映射

    hibernate中的ORM映射文件通常以.hbm.xml作为后缀。使用这个映射文件不仅易读,而且可以手工修改,也可以通过一些工具来生成映射文档,下文给大家详细的介绍hibernate中的对象关系映射,需要的朋友参考下吧
    2017-09-09
  • docusaurus如何添加一个搜索功能

    docusaurus如何添加一个搜索功能

    这篇文章主要介绍了docusaurus如何添加一个搜索功能,本文通过实例图文相结合给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-04-04
  • RabbitMQ基础概念之信道channel详解

    RabbitMQ基础概念之信道channel详解

    这篇文章主要介绍了RabbitMQ基础概念之信道channel详解,信道是生产消费者与rabbit通信的渠道,生产者publish或者消费者消费一个队列都是需要通过信道来通信的,需要的朋友可以参考下
    2023-08-08
  • SpringBoot项目中新增脱敏功能的实例代码

    SpringBoot项目中新增脱敏功能的实例代码

    项目中,由于使用端有两个,对于两个端的数据权限并不一样。Web端可以查看所有数据,小程序端只能查看脱敏后的数据,这篇文章主要介绍了SpringBoot项目中新增脱敏功能,需要的朋友可以参考下
    2022-11-11
  • MyBatis 原生二级缓存"难以修复"的原因解析及解决方案

    MyBatis 原生二级缓存"难以修复"的原因解析及解决方案

    文章主要讨论了MyBatis原生二级缓存存在的问题,包括结构性缺陷、难以修复等,社区提供了多种增强插件方案,但这些方案也有各自的优缺点,本文结合实例代码介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-12-12

最新评论