阿里规约---为什么建议使用ThreadPoolExecutor自定义线程池

创建线程池的四种方式

newSingleThreadExecutor 创建一个拥有一个线程的线程池,该线程池的优点是保证事件的执行顺序(先进先出队列(FIFO)、后进先出(LIFO))

newFixedThreadPool(int nThreads) 创建一个定长线程池,传进去的参数为可同时并发的线程数,超出的任务放进阻塞队列

newScheduledThreadPool(int corePoolSize) 创建一个定长的能执行周期任务的线程池

newCachedThreadPool() 创建可以缓存的线程池,若是事件数小于线程池内线程数,则jvm可回收,反之再创建线程

我们先来看看创建线程池内的构造参数

在这里插入图片描述
corePoolSize:线程池的基本大小(核心线程数)

maximumPoolSize:最大线程数(非核心线程数)

keepAliveTime:线程保持时长,设置线程过期时间

TimeUnit unit:时长单位(TimeUnit.MILLISECONDS、TimeUnit.SECONDS)

workQueue:工作队列

defaultHandler:拒绝策略

下面我们来给大家展开讲每一种线程池的优缺点

newSingleThreadExecutor()

我们先来看看newSingleThreadExecutor()的源码
在这里插入图片描述
从上面的源码我们可以看出,核心线程数为1、最大线程数也为1,所以执行任务时只是一个线程去执行,剩下的加入工作队列,比如我过来了50个任务,只能一个个的去执行,能保证任务的执行顺序,但是执行效率没有其它的线程池高,我们来看看该线程池的结构图

在这里插入图片描述

newFixedThreadPool(int nThreads)

在这里插入图片描述
传进去的int nThreads,是核心线程数,也是最大线程数,线程池结构图
在这里插入图片描述

我们来着重看看这个LinkedBlockingQueue工作队列,先看看它的源码
在这里插入图片描述
它是一个无界队列,也就是说无论多少任务我都照单全收,但是这非常的危险,因为可能会由于线程阻塞等原因,任务执行的慢,可能会造成OOM

newCachedThreadPool()

先来看看源码和结构图,我们从源码结合结构图来分析
在这里插入图片描述
在这里插入图片描述
看着源码和结构图我们能明白,核心线程数为0,来的所有任务会直接塞进阻塞队列(SynchronousQueue)中,什么是阻塞队列呢?看到上面的结构图,阻塞队列只有一个,也就是说有一个任务到阻塞队列中,线程池得立马起一个线程去响应它,要不然它会一直阻塞在队列中,别的任务进不来

newScheduledThreadPool(int corePoolSize)

在这里插入图片描述
调用newScheduledThreadPool(int corePoolSize)的方法时也就会直接调用父类创造线程池的方法,也就是我们下面的这个基本的方法,为什么它可以定时执行呢?最主要还是在它的队列上,new DelayedWorkQueue()
在这里插入图片描述
new DelayedWorkQueue()是一个延时队列,可以根据设置的延时时间取到队列中任务,然后启动线程执行,实现定时执行的效果

现在我们回归正题,为什么java给了我们这么方便使用线程池的方式,阿里还不推荐使用呢?

我们回过头来看看,在newSingleThreadExecutornewFixedThreadPool(int nThreads)使用到的队列,它是一个无界队列啊,我如果有10000W个任务呢???我的天,JVM都快哭了吧,所以说当线程数过多时,没有做到一个很好的限制,全部都往队列里加容易造成OOM,这在正式环境中是一个致命的问题

newScheduledThreadPool(int corePoolSize)newCachedThreadPool()这两个呢?我们来看看它的maximumPoolSize,都为Integer.MAX_VALUE,这样会有一个问题,我还是10000W个任务,我就会起10000W个线程来执行,这都能把电脑的CPU干着火了都

一个是对内存的消耗,还有一个是让CPU的负载过重,所以阿里不推荐这四种创建线程池的方式

阿里支持的是自定义线程的创建,我上代码给大家看看怎么自定义线程
在这里插入图片描述
所以要根据实际任务来自定义线程池,就不会出现上面那四种常规方法创建线程池所出现的问题,但是!!
在这里插入图片描述
有一个小问题,我用一幅图给大家描述一下,最坏的情况
在这里插入图片描述

还有十个任务呢?它们去哪了?我直接说了吧,它会直接报错,这就要涉及到一个知识点了,拒绝策略(ThreadFactory)

举个栗子

public class thread {
    public static void main(String[] args) {

        ExecutorService self = new ThreadPoolExecutor(
                10,
                20,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(20));
        for(int i = 50;i<100;i++){
            self.execute(new MyThread());
        }
    }

}
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(MyThread.currentThread());
    }
}

执行结果:
在这里插入图片描述
会报错,这就是拒绝策略的作用,当我们自定义的线程池接收不了这个任务时,会直接抛出异常,关于里面的源码我就不细讲了,感兴趣的同学可以在下面评论催催更,说不定明天我就更新了呢

在这里插入图片描述
谢谢你们的阅读,给个赞赞赞赞赞来鼓励我吧

完成:TO: 2021/3/30 16:47

<think>好的,我现在需要回答用户关于为什么推荐直接使用ThreadPoolExecutor作为Java线程池实现的问题。首先,我得回顾一下ThreadPoolExecutor的基本知识。ThreadPoolExecutor是Java中用于创建线程池的核心类,它提供了高度可定制的参数,比如核心线程数、最大线程数、存活时间等。用户可能想知道为什么直接使用它而不是其他更高级的工厂方法,比如Executors类提供的newFixedThreadPool或newCachedThreadPool。 首先,我记得Executors工厂方法虽然方便,但可能会隐藏一些配置细节。比如newFixedThreadPool使用无界队列,可能导致内存问题。而直接使用ThreadPoolExecutor可以让开发者显式地设置这些参数,从而更好地控制资源使用。这一点应该作为第一个理由,强调显式控制的重要性。 其次,资源管理的优化。使用ThreadPoolExecutor可以避免Executors中某些预设配置的缺陷,比如newCachedThreadPool可能创建过多线程,导致系统负载过高。直接配置核心线程数、最大线程数和队列类型,能有效防止资源耗尽。这里需要提到不同的队列策略,比如LinkedBlockingQueue和SynchronousQueue的区别,以及如何根据需求选择合适的队列。 第三,拒绝策略的自定义ThreadPoolExecutor允许开发者自定义当任务无法处理时的拒绝策略,比如抛出异常、丢弃任务或由调用线程执行任务。而Executors提供的默认策略可能不符合特定场景需求,比如AbortPolicy直接抛出异常,但在某些情况下可能需要更灵活的处理方式。这里需要举例说明不同的拒绝策略及其适用场景。 第四,线程工厂和钩子方法的扩展性。通过自定义ThreadFactory,可以设置线程的名称、优先级等,便于监控和调试。此外,ThreadPoolExecutor提供的beforeExecute和afterExecute方法允许在任务执行前后添加日志或统计信息,这对性能监控很有帮助。这部分需要说明扩展性带来的好处,比如更好的可维护性。 然后,需要结合用户提供的引用内容。引用[1]中提到ThreadPoolExecutor允许通过方法动态调整参数,比如setCorePoolSize,这提供了运行时调整的灵活性,而Executors创建的线程池可能不支持这些调整。因此,动态调整能力也是一个重要理由。 同时,用户提供的引用[2]和[3]涉及安全相关的异常,虽然与线程池不直接相关,但可以间接说明显式控制配置有助于避免潜在的安全或资源问题,比如通过合理设置线程池参数防止资源耗尽导致系统不稳定。 最后,需要总结直接使用ThreadPoolExecutor的优势,并可能提到在需要高度定制和优化的场景下,直接使用它比使用Executors更合适。同时,确保回答符合用户的要求,比如结构清晰、使用中文、正确的LaTeX格式,以及生成相关问题。 现在需要检查是否有遗漏的点,比如线程池的工作原理,如如何复用线程、keepAliveTime的作用等,但用户问题主要是关于推荐使用的原因,所以可能不需要深入细节。但可以简要提到这些机制如何通过ThreadPoolExecutor的参数进行控制,以支持理由。 另外,用户示例中引用了动态设置参数的方法,如setKeepAliveTime,这也是直接使用ThreadPoolExecutor的优势之一,因为Executors返回的线程池可能不允许这些设置,或者需要类型转换才能访问这些方法。例如,Executors.newFixedThreadPool返回的是ExecutorService接口,而实际实现是ThreadPoolExecutor,但需要强制转换后才能调用这些方法,这可能不太方便。因此,直接使用ThreadPoolExecutor实例可以更方便地调整参数。 总结起来,主要理由包括:显式控制参数、避免资源耗尽、自定义拒绝策略、线程工厂和钩子方法、动态调整能力。这些点需要分条列出,并辅以例子或引用中的内容支持。例如,引用[1]中的方法如setCorePoolSize支持动态调整,这增强了灵活性,而Executors创建的线程池可能需要额外处理才能调整这些参数。 最后,生成的相关问题需要围绕线程池的配置、参数调优、拒绝策略比较、与Executors的对比等方向来设计,确保问题具有针对性和扩展性。</think>### 为什么推荐直接使用ThreadPoolExecutor作为Java线程池实现? 在Java中,创建线程池时推荐直接使用$ThreadPoolExecutor$类而非$Executors$工厂方法,主要原因如下: --- #### 1. **显式控制线程池参数** $ThreadPoolExecutor$允许开发者**直接配置核心参数**,包括: - 核心线程数($corePoolSize$) - 最大线程数($maximumPoolSize$) - 线程存活时间($keepAliveTime$) - 任务队列类型(如$LinkedBlockingQueue$或$SynchronousQueue$) 例如,使用$Executors.newFixedThreadPool()$会默认使用无界队列$LinkedBlockingQueue$,可能导致任务堆积和内存溢出(OOM)。而直接使用$ThreadPoolExecutor$可以显式选择有界队列,避免资源耗尽[^1]。 --- #### 2. **优化资源管理** 通过自定义队列和线程数策略,开发者可以更精细地平衡**性能与资源消耗**: - 有界队列可限制任务数量,防止内存溢出。 - 合理设置$maximumPoolSize$避免过多线程导致系统过载。 - 通过$keepAliveTime$回收空闲线程,减少资源占用[^1]。 --- #### 3. **自定义拒绝策略** $ThreadPoolExecutor$支持自定义$RejectedExecutionHandler$,处理任务队列满时的场景。例如: - **AbortPolicy**:直接抛出异常(默认策略)。 - **CallerRunsPolicy**:由提交任务的线程执行任务。 - **自定义策略**:如记录日志或降级处理。 相比之下,$Executors$的默认策略可能不适用于复杂场景。 --- #### 4. **动态调整能力** 通过$ThreadPoolExecutor$提供的方法(如$setCorePoolSize()$、$setMaximumPoolSize()$),可在运行时**动态调整线程池参数**,适应流量波动。而$Executors$创建的线程池可能需要类型转换才能实现这一功能。 --- #### 5. **扩展性与监控支持** - **自定义线程工厂**($ThreadFactory$):可设置线程名称、优先级等,便于问题排查。 - **钩子方法**(如$beforeExecute()$、$afterExecute()$):支持添加日志、性能监控等逻辑。 --- #### 6. **规避Executors的潜在风险** $Executors$的部分方法存在设计缺陷: - $newCachedThreadPool()$可能因无限创建线程导致CPU负载过高。 - $newSingleThreadExecutor()$使用无界队列,同样有OOM风险。 --- ### 示例代码:直接创建ThreadPoolExecutor ```java int corePoolSize = 5; int maxPoolSize = 10; long keepAliveTime = 60L; BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100); ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue, new CustomThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

沉淀顶峰相见的PET

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值