【Java面试进阶】谈谈为什么禁止使用Executors创建线程池?

Android线程池全面解析:Executors工厂方法与阿里规约的深层思考 文章摘要: Android开发中合理使用线程池对应用性能至关重要。Executors提供的四种线程池(FixedThreadPool、CachedThreadPool、SingleThreadExecutor和ScheduledThreadPool)各有特点,但都暗藏风险:无界队列可能导致OOM,无限创建线程会耗尽资源。阿里Java规约建议手动创建线程池,通过控制核心参数(线程数、队列容量、拒绝策略)规避风险。最佳实践包括:使用有界队列、限制最大线程数、自定义线程工厂、选择合适的拒绝策略(如CallerRu 阅读详情
AI摘要

Executors是Java中用于创建线程池的工具类,提供了newFixedThreadPool、newCachedThreadPool等工厂方法。然而,这些方法可能导致资源耗尽,如固定线程池可能因请求队列无界而引发OOM,缓存线程池可能创建大量线程。为避免这些问题,应该避免直接使用Executors,而是通过ThreadPoolExecutor构造函数自定义线程池,设定合理的参数,如核心线程数、最大线程数和队列容量,以确保线程池的稳定和高效运行。


首先, 可以通过Executors静态工厂构建线程池,但是一般不建议这样使用。

什么是Executors?

Executors是一个Java中的工具类。提供工厂方法来创建不同类型的线程池。如下所示:
Executors源码
从源码中可知,Executors的创建线程池的方法,创建出来的线程池都实现了ExecutorService接口。常用方法有以下几个:

  • newFixedThreadPool(int nThreads):创建固定数目线程的线程池。
  • newCachedThreadPool():创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到线程池中。终止并从缓存中移除那些已有60秒钟未被使用的线程。
  • newSingleThreadExecutor():创建一个单线程化的Executor。
  • newScheduledThreadPool(int corePoolSize):创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

从源码看,该类的功能还是非常强大的,而且还用到了工厂模式,具有比较强的扩展性,用起来也方便,例如:

ExecutorService executor = Executors.newFixedThreadPool(nThreads);

即可创建一个固定大小的线程池。

但是:在开发中不允许使用Executors去创建线程池,而是通过ThreadPoolExecutor的方式,这样可以避免资源耗尽的风险。原因是:

  1. FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
  2. CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

Executors存在什么问题?

首先看一个简单的例子,模拟Executors导致OOM的情况:

package com.seckill.secondkill;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

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
            e.printStackTrace();
        }
    }
}

指定JVM运行参数:-Xmx8m -Xms8m, 然后运行代码,会抛出OOM:
OOM异常
上面异常表示在main函数的线程中存在未捕获的异常。

Executors为什么存在缺陷?

我们分析newFixedThreadPool的底层源码,真正导致OOM的其实是LinkedBlockingQueue.offer方法:

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

这里补充Java中的阻塞队列的知识:
Java中的BlockingQueue主要有两种实现,分别是ArrayBlockingQueue和LinkedBlockingQueue。

  • ArrayBlockingQueue是一个用数组实现的有界阻塞队列,必须设置容量。
  • LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE.

因此,这里就出现了一个问题:如果我们不设置 LinkedBlockingQueue 的容量的话,其默认容量将会是 Integer.MAX_VALUE。而newFixedThreadPool中创建LinkedBlockingQueue时,并未指定容量。此时,LinkedBlockingQueue就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况就有可能因为任务过多导致内存溢出问题。

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

如何正确创建线程池?

避免使用Executors创建线程池,主要是避免使用其中的默认实现。我们可以自己直接调用ThreadPoolExecutor的构造函数来自己创建线程池,在创建的同时,给BlockQueue指定容量就可以。

private static ExecutorService executor = 
            new ThreadPoolExecutor(10,10,60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));

这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出java.util.concurrent.RejectedExecutionException,这是因为当前线程池使用的队列是有界队列,队列已经满了便无法继续处理新的请求。但是异常(Exception)总比错误好。

当然,除了自己定义ThreadPoolExecutor外,还可以使用一些开源类库,比如apache和guava等。这里推荐使用guava提供的ThreadFactoryBuilder来创建线程池。示例如下:

package com.seckill.secondkill;


import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;


public class ExecutorsDemo {
    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("demo-pool-%d").build();
    
    private static ExecutorService pool = new ThreadPoolExecutor(5, 100,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            pool.execute(new SubThread());
        }
        System.out.println("END");
    }
}

class SubThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getState() + "----------" + Thread.currentThread().getName());
    }
}

执行过程中的线程数:
工作线程数
通过上述方式创建线程时,不仅可以避免 OOM 的问题,还可以自定义线程名称,更加方便的出错的时候溯源。

一位大厂面试官的灵魂发问:Executor 和 Executors 有什么区别? 那次面试,我虽然在“Executor vs Executors”这一题没拿满分,但却在复盘中成长巨大。很多时候,不是我们不会,而是没“透过源码看本质”。“Executors 让你写得快,ThreadPoolExecutor 让你跑得稳。别再让“无界队列”、“线程爆炸”、“OOM”等等这些坑在生产中炸锅。写代码,不只是能跑,而是能跑得久、跑得稳。 阅读详情

相关推荐

行为验证码 AJ-Captcha 使用文档

AJ-Captcha验证码系统集成指南:需添加captcha-spring-boot-starter和Redis依赖,配置Redis连接。核心实现包括Redis缓存服务类CaptchaCacheRedisImpl(含设置、查询、删除验证码等方法)、SPI配置文件及Spring Boot配置类(配置验证码属性和Redis模板)。系统支持验证码生成、校验、防刷等安全功能,适用于Web应用安全验证场景。

千层冷面的博客 2110

Executors一篇就够

Executors框架包含的内容十分的多:看图: Executors框架前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、pandas是什么? 示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。 二、使用步骤 1.引入库 代码

qq_44828283的博客 4889

安鸾渗透测试平台-CTF系列-bash

工具Xshell 公网VPS接收反弹shell Xshell新手的下载及安装教程(超详细)_新手程序员攀爬之路的博客-优快云博客_xshell使用教程 Xshell连接后 #!/bin/bash cat ${0} while : do echo "$" read input output=`$input` 显示了一个文件,不过咋做呢?这里可能考察的是linux的语法 方法 输入bash 获得shell cat ${0} while : do .

AAAAAAAAAAAA66的博客 2689

专项攻克——线程池

文章目录五种线程池的使用场景线程池的继承关系线程池的具体实现类ThreadPoolExecutor的重要参数==线程池的几步主要工作流程思路分析==线程池都有哪几种工作队列 多线程面试问题 参考博客 五种线程池的使用场景 一个使用线程池管理线程的例子://创建一个可重用固定线程数量的线程池 ExecutorService pool = Executors.newFixedThreadPool...

军威兄的博客 7046

scGPT学习资料汇总 - 单细胞多组学基础模型

scGPT(single-cell Generative Pre-trained Transformer)是一个针对单细胞多组学数据的基础模型,由多伦多大学Wang Bo实验室开发。该模型通过在超过3300万个细胞上进行生成式预训练,捕捉了基因和细胞之间的复杂关系,为多种下游任务提供了强大的基础。scGPT为单细胞分析提供了强大的基础模型和丰富的资源。通过本文汇总的学习材料,读者可以快速掌握scGPT的使用,并将其应用到自己的研究中。

2401_87189860的博客 838

LabVIEW工程密码保护机制与移除技术详解

LabVIEW作为工业自动化领域广泛使用的图形化编程工具,其密码保护机制涉及文件加密与权限控制等核心技术。从技术实现来看,现代版本采用AES-128加密结合SHA-256哈希校验,而经典版本使用XOR加密配合Base64编码。理解这些加密原理对于工程维护、灾难恢复等合法场景至关重要。在实际应用中,密码移除工具需要处理不同版本的文件结构差异,包括解析XML描述区的Protection标签、动态识别版本并应用对应解密策略。这类技术在工业设备维护、教学研究等场景具有重要价值,但必须注意遵守法律边界,确保拥有合法的

weixin_34161083的博客 390

多线程理解(二)

线程安全的问题 关于线程安全问题,有一个经典的问题----银行取钱的问题。银行取钱的基本流程如下: 用户输入账户、密码,系统判断用户的账户、密码是否匹配; 用户输入取款金额; 系统判断账户余额是否大于取款金额; 如果余额大于取款金额,则取款成功;如果余额小于取款金额,则取款失败; 如果将这个流程放在多线程并发的场景下,就有可能出现问题; 同步代码块 为了解决这个问题,Java的多线程支持引入了...

逝去的往事的博客 6357

NAT反向代理技术的实现(外网访问内网)

NAT反向代理技术的实现(外网访问内网)(版权所有,引用 请联系作者,注明出处) NAT技术是网络中的重要应用之一。当有一个场景:内网搭建了服务器,需要外网访问时,可以借助NAT映射来实现。如:单位建立了一个Web服务器,我们希望在员工回家之后,依然可以访问到它,那怎么实现呢? 下面的拓扑图,就用来示范这个应用反面场景的实现过程。 一、题目要求: 地址配置如图。使用静态路由: 1、配置右侧交换机2950T,实现单臂路由; 2、配置路由使网络连通; 3、配置NAT访问,实现外网对风网服务器的

joyousmime的博客 8487

线程池Executors.newFixedThreadPool

线程池Executors.newFixedThreadPool

bobo8945510的博客 4万+

Xilinx DDR3 MIG IP核设计--汇总导航

快速上手Xilinx DDR3 IP核----汇总篇

孤独的单刀 2万+

【Flink】FlinkSQL如何触发执行计算

Table (通过 StatementSet.addInsert() 输出给某个 Sink)和 INSERT 语句 (通过调用 StatementSet.addInsertSql())会先被缓存到 StatementSet 中,StatementSet.execute() 方法被调用时,所有的 sink 会被优化成一张有向无环图。该方法是用来将一个表的内容插入到目标表中,一旦该方法被调用, TABLE API 程序立即被翻译。该方法是用来执行一个 SQL 语句,一旦该方法被调用, SQL 语句立即被翻译。

一杯咖啡半杯糖的博客 1006

多线程基础(九)Executors线程池

Executors线程池

m0_47743175的博客 2186

java.sql.SQLIntegrityConstraintViolationException: #23000

java.sql.SQLIntegrityConstraintViolationException: #23000**异常信息****异常原因****解决方法** 异常信息 ### Cause: java.sql.SQLIntegrityConstraintViolationException: #23000 ; #23000; nested exception is java.sql.SQLIntegrityConstraintViolationException: #23000] with root c

123 4352

面试官:为什么《阿里巴巴Java开发手册》上要禁止使用Executors来创建线程池

面试官:为什么《阿里巴巴Java开发手册》上要禁止使用Executors来创建线程池

天堂的博客 3078

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

线程池创建陷阱:为什么阿里禁止使用Executors?

Executors 的工厂方法虽然方便,但隐藏了线程池的核心参数和配置,容易导致资源耗尽、拒绝策略不灵活、难以监控和调优等问题。相比之下,直接使用 ThreadPoolExecutor 构造函数创建线程池,可以更精细地控制线程池的行为,更好地适应实际业务需求。

xujunson的博客 1125

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

为什么阿里巴巴Java开发手册禁止使用Executors创建线程池?

在Java并发编程中,线程池是提升系统性能的重要工具,但使用Executors工厂方法(如newFixedThreadPool和newCachedThreadPool)创建线程池存在潜在风险。这些方法默认使用无界队列或无限线程数,可能导致内存溢出或系统资源耗尽,尤其是在高并发场景下。《阿里巴巴Java开发手册》明确禁止使用Executors创建线程池,建议通过ThreadPoolExecutor手动配置线程池参数,如核心线程数、最大线程数、队列容量和拒绝策略,以更好地控制资源使用并避免系统崩溃。例如,在电商

leader_song的博客 1155

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

为什么阿里的Java开发规范中禁止使用Executors创建线程池?

最近壹哥有个学生出去面试,面试官的一个问题是:在开发中你使用什么方式创建线程池?这个学生答曰:使用jdk中自带的工厂类Executors 创建线程池!该学生回答完问题后,感觉面试官对此答案不是很满意,于是就跑回来问壹哥。那么接下来,壹哥就和大家来分享一下这道面试题的标准答案。 其实这个问题的答案,在阿里巴巴开发规范1.4版中,早就有明确的答案,我们先来看看阿里巴巴开发规范中对于这一段描述的截图:从上图中我们不难看出,创建线程池,最好直接使用 ThreadPoolExecutor 类的构造方法去创建!那么为什

一一哥 1482

大厂面试丨为什么阿里的Java开发规范,禁止使用Executors创建线程池?!

一. 问题概述最近耀哥有个学生出去面试,面试官的一个问题是:在开发中你使用什么方式创建线程池?这个学生答曰:使用jdk中自带的工厂类Executors 创建线程池!该学生回答完问题后,感觉面试官对此答案不是很满意,于是就跑回来问耀哥。那么接下来,耀哥就和大家来分享一下这道面试题的标准答案,看看怎么回答才能拿高分!二. 问题的答案其实这个问题的答案,在阿里巴巴开发规范1.4版中,早就有明确的答案,我们先来看看阿里巴巴开发规范中对于这一段描述的截图:从上图中我们不难看出,创建线程池,最好直接使用 ThreadP

威哥爱编程,华为 HDE,鸿蒙极客、优快云博客专家、《鸿蒙 HarmonyOS 5/6 开发之路》系列图书第一作者 924

为什么不建议通过 Executors 构建线程池?(附原理剖析 + 实战案例)

Executors工具类虽然提供了快速创建线程池的方法,但存在严重隐患。其默认使用无界队列(LikedBlockingQueue),在任务激增时会导致内存暴涨甚至OOM;同时屏蔽了拒绝策略等关键参数设置,难以监控和调试。阿里巴巴开发手册明确禁止使用Executors,建议通过ThreadPoolExecutor手动配置有界队列、合理的线程数、拒绝策略和线程命名等,才能构建健壮的线程池。文章通过源码分析、风险说明和实战案例,揭示了Executors的潜在问题,并给出了最佳实践方案,强调合理参数配置和线程隔离的

yiridancan的博客 1314

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

点击上方“方志朋”,选择“设为星标”回复”666“获取新整理的面试资料作者:何甜甜在吗juejin.im/post/5dc41c165188257bad4d9e69看阿里巴巴开发手册并发编...

方志朋的博客 127

线程池面试必问:核心参数、执行流程、为什么阿里禁止用Executors?

线程池七参数:核心最大存活时,队列工厂拒绝策;执行四步走:核心队列最大拒;Executors不要用,无界队列OOM;CPU密集N+1,IO密集2N起;shutdown等任务,shutdownNow强中断。七大参数是什么 → 执行流程四步走 → 为什么不用 Executors。把这三条讲清楚,再加上拒绝策略和配置调优,线程池就稳了。下周一我们讲JUC 并发工具(CountDownLatch、CyclicBarrier、Semaphore),敬请关注。如果这篇对你有帮助,欢迎。

qq_33981280的博客 375

阿里规范为什么禁止用 Executors?线程池创建的 5 种方式,90% 的人都踩过坑

Executors 的快捷方法隐藏了关键参数,让你无法感知到队列无界或线程数无界的风险。生产环境必须用。

2301_79963518的博客 414

Java面试题,Java,面试题,线程池

Java面试题:线程池

有人和我一样懒 2486

线程池面试题

线程池面试题什么是线程池为什么要使用线程池实际项目中哪些地方会使用线程池线程池的作用创建线程池的Executors封装方法(不建议使用)线程池底层实现原理线程池中的线程是一直运行的吗四种拒绝策略为什么不建议使用Executors创建线程池 深入理解线程池底层原理 什么是线程池 统一管理和维护线程,减少创建和销毁线程的开销 为什么要使用线程池 在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请

aimerary的博客 6583

[java八股文][Java并发编程面试篇]线程池

线程池是为了减少频繁的创建线程和销毁线程带来的性能损耗,线程池的工作原理如下图:线程池分为核心线程池,线程池的最大容量,还有等待任务的队列,提交一个任务,如果核心线程没有满,就创建一个线程,如果满了,就是会加入等待队列,如果等待队列满了,就会增加线程,如果达到最大线程数量,如果都达到最大线程数量,就会按照一些丢弃的策略进行处理。提交任务 → 核心线程是否已满?├─ 未满 → 创建核心线程执行└─ 已满 → 任务入队├─ 队列未满 → 等待执行└─ 队列已满 → 创建非核心线程。

hzx2400的博客 1208

boost创建线程池_一篇文章带你完全了解JAVA线程池,再也不用担心被面试官问了!...

1.什么是线程池线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位,我们的程序最终都是由线程进行运作。在Java中,创建和销毁线程的动作是很消耗资源的,因此就出现了所谓“池化资源”技术。线程池是池化资源技术的一个应用,所谓线程池,顾名思义就是预先按某个规定创建若干个可执行线程放入一个容器中(线程池),需要使用的时候从线程池中去取,用完之后不销毁而是...

weixin_29419979的博客 135
上一篇: 【Java进阶面试】如何实现一个避免消息重复消费的方案?
下一篇: 【Java面试进阶】谈一谈为什么禁止使用BigDecimal的equals方法做等值比较?
镰刀韭菜
博客等级 码龄12年 2万+粉丝 853原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

镰刀韭菜

看在我不断努力的份上,支持我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值