Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >为什么线程池不允许使用Executors去创建?

为什么线程池不允许使用Executors去创建?

作者头像
六月的雨在Tencent
发布于 2024-03-28 13:19:38
发布于 2024-03-28 13:19:38
2870
举报
文章被收录于专栏:CSDNCSDN
为什么线程池不允许使用Executors去创建?

Executors

Executors 是一个Java中的工具类。提供工厂方法来创建不同类型的线程池。

Executors的创建线程池的方法,创建出来的线程池都实现了ExecutorService接口。常用方法有以下几个:

代码语言:javascript
AI代码解释
复制
//创建固定数目线程的线程池
ExecutorService executor1 = Executors.newFixedThreadPool(8);
//创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个
//新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
ExecutorService executor2 = Executors.newCachedThreadPool();
//创建一个单线程化的Executor
ExecutorService executor3 = Executors.newSingleThreadExecutor();
//创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类
ScheduledExecutorService executor4 = Executors.newScheduledThreadPool(8);

这个类整体来说使用起来比较方便,但是为什么说不建议用,下面来看阿里社区Java开发规范中的强制约束:

Executors存在什么问题

针对阿里Java开发手册提到的OOM问题,先模拟一段程序

代码语言:javascript
AI代码解释
复制
public class ExecutorsDemoController {

    private static ExecutorService executor = Executors.newFixedThreadPool(8);

    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 {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(1000000);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

通过指定JVM参数:-Xmx8m -Xms8m 运行以上代码,会抛出OOM: 执行结果:

错误信息:

代码语言:javascript
AI代码解释
复制
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.dongao.project.danotice.controller.ExecutorsDemoController.main(ExecutorsDemoController.java:17)

代码中的第17行发生异常。

Executors为什么会OOM

通过上面的报错信息可以看出,其中真正导致OOM的是LinkedBlockingQueue.offer(E e),查看Executors.newFixedThreadPool(int nThreads)的源码,看到阻塞队列用的是new LinkedBlockingQueue<Runnable>();

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。

创建线程池的正确姿势

避免使用Executors创建线程主要是避免其中一些参数给的默认值,那么可以直接用ThreadPoolExecutor创建线程,并且指定具体的参数值。

代码语言:javascript
AI代码解释
复制
    private static ExecutorService execute = new ThreadPoolExecutor(10,10,60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(8));

根据ThreadPoolExecutor构造函数中参数,具体参数说明如下:

corePoolSize:线程池中核心线程数的最大值 maximumPoolSize:线程池中能拥有最多线程数 keepAliveTime:表示空闲线程的存活时间 unit:表示keepAliveTime的单位 workQueue:用于缓存任务的阻塞队列 此处还有一个默认参数: handler:表示当 workQueue 已满,且池中的线程数达到 maximumPoolSize 时,线程池拒绝添加新任务时采取的策略,默认如下:

代码语言:javascript
AI代码解释
复制
private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

表示:抛出RejectedExecutionException异常 或者也可以如下创建线程:

代码语言:javascript
AI代码解释
复制
private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("pool-%d").build();
    private static ExecutorService executor = new ThreadPoolExecutor(10,10,60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(8),namedThreadFactory);

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Java 线程池】Java 创建线程池的正确姿势: Executors 和 ThreadPoolExecutor 详解
Executors的创建线程池的方法,创建出来的线程池都实现了ExecutorService接口。常用方法有以下几个:
一个会写诗的程序员
2020/06/03
38.2K0
【小家java】Java中的线程池,你真的用对了吗?(教你用正确的姿势使用线程池,Executors使用中的坑)
在【小家java】用 ThreadPoolExecutor/ThreadPoolTaskExecutor 线程池技术提高系统吞吐量(附带线程池参数详解和使用注意事项)这篇文章中,我们介绍过了Java中线程池的常见用法以及基本原理。
YourBatman
2019/09/03
1.9K0
【小家java】Java中的线程池,你真的用对了吗?(教你用正确的姿势使用线程池,Executors使用中的坑)
关于禁止使用Executors创建线程池的分析
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 注意,这里的重点是 不允许。而不是不建议。可见该规范 背后都是血淋淋的生产事故。
冬天里的懒猫
2020/08/04
1.6K0
关于禁止使用Executors创建线程池的分析
Java陷阱之:线程池不允许使用 Executors 去创建
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,合理设置7大参数。
崔认知
2023/06/19
4050
Java陷阱之:线程池不允许使用 Executors 去创建
Executors Java编程规范插件提示手动创建线程池的解决办法
https://howtodoinjava.com/core-java/multi-threading/when-to-use-countdownlatch-java-concurrency-example-tutorial/
明明如月学长
2021/08/27
6210
About ExecutorService(2),自定义线程池
琢磨了一下,还是把这篇提前了,本片篇幅可能会有些长,甚至冗余,请各位看官原谅我这拙劣的写作能力。
小鄧子
2018/08/20
6690
About ExecutorService(2),自定义线程池
并发编程之线程池的使用
并发编程之线程池的使用 转载自http://www.cnblogs.com/dolphin0520/p/3932921.html https://blog.csdn.net/wanghao_0206/article/details/76460877 Java中的ThreadPoolExecutor类 java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExe
爱撒谎的男孩
2018/07/04
4080
多线程(三) | 彻底搞懂线程池-ThreadPoolExecutor
上篇文章我们讲解了Executors这个线程池工厂类的用法,介绍了它所提供的一些常用线程池工具的用法,今天我们来继续深入学习。
一缕82年的清风
2022/05/10
1K0
多线程(三) | 彻底搞懂线程池-ThreadPoolExecutor
java线程池(一):java线程池基本使用及Executors
在前面学习线程组的时候就提到过线程池。实际上线程组在我们的日常工作中已经不太会用到,但是线程池恰恰相反,是我们日常工作中必不可少的工具之一。现在开始对线程池的使用,以及底层ThreadPoolExecutor的源码进行分析。
冬天里的懒猫
2020/09/17
1.5K0
java线程池(一):java线程池基本使用及Executors
Java高并发之线程池详解
在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理。例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对象, 那么系统效率将大大提升。另外一个好处是可以设定池化对象的上限, 例如预防创建线程数量过多导致系统崩溃的场景.
用户1269200
2018/07/30
4860
Java高并发之线程池详解
多线程的使用,避免ExecutorService 的创建处理
如果读者对Java 中的阻塞队列有所了解的话,看到这里或许就能够明白原 因了。 Java 中的BlockingQueue 主要有两种实现, 分别是ArrayBlockingQueue 和 LinkedBlockingQueue。 ArrayBlockingQueue 是一个用数组实现的有界阻塞队列,必须设置容量。 LinkedBlockingQueue 是一个用链表实现的有界阻塞队列,容量可以选择 进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_ VALUE。 这里的问题就出在:不设置的话,将是一个无边界的阻塞队列,最大长度为 为什么阿里巴巴禁止使用Executors 创建线程池? <  35 Integer.MAX_VALUE。也就是说,如果我们不设置LinkedBlockingQueue 的 容量的话,其默认容量将会是Integer.MAX_VALUE。 而newFixedThreadPool 中创建LinkedBlockingQueue 时,并未指定容 量。此时,LinkedBlockingQueue 就是一个无边界队列,对于一个无边界队列 来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导 致内存溢出问题。 上面提到的问题主要体现在newFixedThreadPool 和newSingleThreadExecutor 两个工厂方法上,并不是说newCachedThreadPool 和newScheduledThreadPool 这两个方法就安全了,这两种方式创建的最大线程数可能是 Integer.MAX_VALUE,而创建这么多线程,必然就有可能导致OOM。
小马哥学JAVA
2022/11/21
1.1K0
Java的Executor框架和线程池实现原理
Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法,它没有实现类只有另一个重要的子接口ExecutorService
全栈程序员站长
2022/11/17
5540
Java的Executor框架和线程池实现原理
线程池:治理线程的法宝
在当今计算机的CPU计算速度非常快的情况下,为了能够充分利用CPU性能提高程序运行效率我们在程序中使用了线程。但是在高并发情况下会频繁的创建和销毁线程,这样就变相的阻碍了程序的执行速度,所以为了管理线程资源和减少线程创建以及销毁的性能消耗就引入了线程池。
用户1516716
2020/02/20
9130
线程池:治理线程的法宝
Executors源码之线程池
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
二十三年蝉
2021/06/29
4990
一篇搞懂线程池
在上一篇文章《spring boot使用@Async异步任务》中我们了解了使用@Async的异步任务使用,在这篇文章中我们将学习使用线程池来创建异步任务的线程。
小森啦啦啦
2019/07/14
7570
Java线程池
线程池,顾名思义,这是管理一堆线程而出现的对象。与数据库的连接池一致,它的出现解决了线程的频繁创建和销毁,从而浪费大量资源的问题。
半月无霜
2023/03/03
7080
Java线程池
相关推荐
【Java 线程池】Java 创建线程池的正确姿势: Executors 和 ThreadPoolExecutor 详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档