使用newCachedThreadPool创建线程池——造成OOM(内存溢出)

一、Java通过Executors提供四种线程池,分别为:

1、newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

2、newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

3、newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。

4、newSingleThreadExecutor: 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

 

二、newCachedThreadPool 造成OOM解析

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

public class newCachedThreadPoolTest {
 
    public static void main(String[] args) {
 
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 1; i < 10000; i++)
            executorService.submit(new task());
 
    }
 
}
 
class task implements Runnable {
 
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
    }
}

运行结果为:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:714)
    at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1368)
    at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
    at com.wenniuwuren.concurrent.newCachedThreadPoolTest.main(newCachedThreadPoolTest.java:15)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

可以看出来是堆外内存溢出,因为我们新建的线程都在工作(代码中用sleep表示在工作中),newCachedThreadPool 只会重用空闲并且可用的线程,所以上述代码只能不停地创建新线程,在 64-bit JDK 1.7 中 -Xss 默认是 1024k,也就是 1M,那就是需要 10000*1M = 10G 的堆外内存空间来给线程使用,但是我的机器总共就 8G 内存,不够创建新的线程,所以就 OOM 了。

 

总结一下:所以这个newCachedThreadPool 大家一般不用就是这样的原因,因为它的最大值是在初始化的时候设置为Integer.MAX_VALUE,一般来说机器都没那么大内存给它不断使用。当然知道可能出问题的点,就可以去重写一个方法限制一下这个最大值,但是出于后期维护原因,一般来说用 newFixedThreadPool 也就足够了。

<think>嗯,用户问的是使用Executors创建线程池和直接使用ThreadPoolExecutor的区别。首先,我得回想一下Java中线程池的相关知识。记得Executors工具类提供了很多静态方法来创建不同类型的线程池,比如newFixedThreadPool、newCachedThreadPool这些。而ThreadPoolExecutor则是更底层的类,允许更详细的参数配置。 那用户可能想知道这两种方式在使用上的区别,或者为什么有时候推荐用ThreadPoolExecutor而不是Executors。可能涉及到参数配置的灵活性问题,或者Executors某些方法可能隐藏了一些潜在的问题,比如资源耗尽的风险。比如newCachedThreadPool可能会创建过多线程,而newFixedThreadPool使用的无界队列可能导致OOM。 接下来需要整理两者的区别,包括参数控制、资源管理、以及最佳实践。比如Executors的方法可能隐藏了具体的参数设置,而直接使用ThreadPoolExecutor可以让开发者明确指定核心线程数、最大线程数、队列类型等,从而避免资源问题。另外阿里巴巴的开发手册好像提到过不建议使用Executors,而推荐ThreadPoolExecutor,这点也应该提到。 还要给出代码示例,比如用Executors创建和用ThreadPoolExecutor直接创建的对比。然后解释每个参数的意义,比如corePoolSize、maximumPoolSize、keepAliveTime、workQueue,以及RejectedExecutionHandler的作用。 然后,在相关问题部分,需要设计几个相关问题,比如线程池参数的意义、四种线程池的区别、无界队列的问题,以及拒绝策略的类型。这些都能帮助用户更深入理解线程池的配置和潜在问题。</think><< 使用`Executors`工具类创建线程池和直接使用`ThreadPoolExecutor`的区别主要体现在对线程池参数的透明性和资源控制能力上。以下是具体区别和示例: ### 1. **参数控制与默认行为** - **Executors**: - 提供预定义的线程池类型(如`newFixedThreadPool`, `newCachedThreadPool`等),但隐藏了底层参数细节。 - 某些方法可能使用默认的无界队列(如`LinkedBlockingQueue`),可能导致内存溢出OOM)。 ```java // 使用Executors创建固定线程池(无界队列) ExecutorService executor = Executors.newFixedThreadPool(10); ``` - `newCachedThreadPool`默认使用`SynchronousQueue`,允许无限线程增长(最大为`Integer.MAX_VALUE`),可能耗尽系统资源。 - **ThreadPoolExecutor**: - 需要手动指定核心参数(核心线程数、最大线程数、队列类型、拒绝策略等),对资源控制更透明。 ```java // 手动创建ThreadPoolExecutor(有界队列 + 明确拒绝策略) ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, // 核心线程数 10, // 最大线程数 60L, TimeUnit.SECONDS, // 空闲线程存活时间 new ArrayBlockingQueue<>(100), // 有界队列(容量100) new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); ``` ### 2. **资源管理风险** - **Executors**的`newFixedThreadPool`和`newSingleThreadExecutor`使用无界队列(默认`Integer.MAX_VALUE`),任务堆积时可能触发OOM。 - **ThreadPoolExecutor**通过有界队列(如`ArrayBlockingQueue`)和自定义拒绝策略(如`AbortPolicy`),可主动避免资源耗尽。 ### 3. **最佳实践** - 阿里巴巴《Java开发手册》明确禁止使用`Executors`创建线程池,推荐直接使用`ThreadPoolExecutor`,以明确线程池行为。 ### 示例对比 ```java // 不安全的Executors方式(无界队列) ExecutorService unsafeExecutor = Executors.newFixedThreadPool(10); // 安全的ThreadPoolExecutor方式(有界队列 + 拒绝策略) ExecutorService safeExecutor = new ThreadPoolExecutor( 5, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.AbortPolicy() ); ``` ### 核心区别总结 | 特性 | Executors | ThreadPoolExecutor | |---------------------|-------------------------------|-------------------------------| | **参数透明度** | 隐藏底层参数 | 完全透明,需手动指定所有参数 | | **队列类型** | 可能使用无界队列(如`LinkedBlockingQueue`) | 可灵活选择有界/无界队列 | | **资源控制** | 默认配置易引发OOM | 显式控制队列和线程数量 | | **拒绝策略** | 默认`AbortPolicy` | 可自定义(如`CallerRunsPolicy`) |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值