通过一次代码校验发现 线程池不建议使用Executors去创建,而是通过ThreadPoolExecutor方式的原因 顺便总结线程优缺点

原文链接:https://blog.youkuaiyun.com/qq_31615049/article/details/80756781

今天在用P3C检查代码的时候发现这样一个警告:

首先用的是new Thread
public void doSummaryJob() throws Exception {
        try{
            HandlerMappingSummaryJobRunnable handlerMappingSummaryJobRunnable = new HandlerMappingSummaryJobRunnable();
            Thread summaryJobThread = new Thread(handlerMappingSummaryJobRunnable);
            summaryJobThread.start();
        }catch(Exception e){
            throw new Exception(HandlerMappingStaticValue.LOG_DO_SUMMARY_JOB_ERROR+e.getMessage());
        }
    }
然后爆出下面的建议

于是我改成Executors工厂创建线程池
HandlerMappingSummaryJobRunnable handlerMappingSummaryJobRunnable = new HandlerMappingSummaryJobRunnable();
             // 定义一个线程池
            ExecutorService executor = Executors.newCachedThreadPool();
                executor.execute(() -> {
                    try {
                        handlerMappingSummaryJobRunnable.run();//执行 方法
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally{
                        executor.shutdown();// 关闭线程池
                    }
                });

继续爆出下面的问题

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

最终我使用ThreadPoolExecutor
            //构造一个线程池
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                            1,1,10,TimeUnit.SECONDS, 
                            new ArrayBlockingQueue<Runnable>(1),
                            new ThreadPoolExecutor.DiscardOldestPolicy());
            threadPool.execute(() -> {
                    try {
                            handlerMappingSummaryJobRunnable.run();//执行 方法
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally{
                    threadPool.shutdown();// 关闭线程池
                    }
                });
阿里爸爸终于不再教育我了。。。

下面来看一下构造器,了解一下参数含义

//            public ThreadPoolExecutor(
//                  int corePoolSize, - 线程池核心池的大小。
//                              int maximumPoolSize, - 线程池的最大线程数。
//                              long keepAliveTime, - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
//                              TimeUnit unit, - keepAliveTime 的时间单位。
//                              BlockingQueue<Runnable> workQueue, - 用来储存等待执行任务的队列。
//                              ThreadFactory threadFactory, - 线程工厂。

//                              RejectedExecutionHandler handler)  - 拒绝策略。

由此开始简要概述一下创建线程的几种方式及优劣

new Thread 方式
优势:显而易见的线程创建方式

缺点:每次都要new对象,当有大量请求时,数不清new了多少个对象了,如果不及时关闭会导致内存溢出,因此还要考虑线程管理等问题。

Executors工厂创建线程池
newCachedThreadPool:

创建一个可缓存线程池

优点:很灵活,弹性的线程池线程管理,用多少线程给多大的线程池,不用后及时回收,用则新建

缺点:一旦线程无限增长,会导致内存溢出。

newFixedThreadPool :

优点:创建一个固定大小线程池,超出的线程会在队列中等待。

缺点:不支持自定义拒绝策略,大小固定,难以扩展

newScheduledThreadPool :

优点:创建一个固定大小线程池,可以定时或周期性的执行任务。

缺点:任务是单线程方式执行,一旦一个任务失败其他任务也受影响

newSingleThreadExecutor :

优点:创建一个单线程的线程池,保证线程的顺序执行

缺点:不适合并发。。不懂为什么这种操作要用线程池。。为什么不直接用队列

统一缺点:不支持自定义拒绝策略。

通过ThreadPoolExecutor创建线程池
优点:集上述优点于一身

缺点:没发现缺点,因为上述线程池的底层就是通过它来创建的。。哈哈哈

上源码举个例子!

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

    }

你会发现Executors的底层就是ThreadPoolExecutor。。只不过加了很多限制,剩下的自己翻源码去!!
————————————————
补充:
当任务依赖上一个任务的结果时,就可以考虑 SingleThreadExecutor

 

 

<think>好的,我需要帮用户解决如何定义一个灵活且可复用的Java线程池工具类,支持同场景创建同类型的线程池,并允许动态调整参数。首先,我得回顾一下Java线程池的基础知识,特别是ThreadPoolExecutor的构造参数,比如核心线程数、最大线程数、队列类型等。根据用户提供的引用内容,Executors工具类提供了newFixedThreadPool等方法,但用户需要更灵活的自定义实现。 接下来,用户提到动态调整参数,这可能涉及到在运行时修改线程池的核心参数。我记得ThreadPoolExecutor本身提供了一些set方法,比如setCorePoolSize,允许动态调整核心线程数。但需要确认这些方法的线程安全性使用限制。此外,工具类需要支持同类型的线程池,比如固定大小、缓存、定时任务等,可能需要通过工厂模式来封装同的创建逻辑。 然后,复用线程池是关键。工具类应该管理已创建线程池实例,避免重复创建,可能使用Map来保存同类型的线程池,并确保线程池的配置可调整。同时,需要考虑异常处理,比如用户提供的引用中提到的,如果参数小于等于0要抛异常,这点需要注意在工具类中进行参数校验。 动态调整参数部分,可能需要提供公共方法,如调整核心线程数、最大线程数,或者队列容量。这里要处理并发问题,确保调整时线程池状态一致。另外,监控线程池的状态也很重要,比如当前活跃线程数、队列大小等,方便用户了解运行状况。 关于最佳实践,应该遵循线程池配置的最佳实践,比如避免使用无界队列,合理设置拒绝策略,根据任务类型选择同的队列类型。例如,CPU密集型任务IO密集型任务的线程池配置可能同,工具类需要支持这些场景。 最后,需要参考用户提供的引用内容,特别是引用[3]中的ThreadPoolUtils,可能需要扩展其功能,添加动态调整的方法。同时,结合引用[4]中的构造参数,确保工具类能够灵活配置这些参数。代码示例部分需要展示如何创建线程池、调整参数,以及使用工厂方法生成同类型的实例。 总结步骤:1. 设计线程池工厂类,支持同类型线程池创建。2. 实现参数动态调整方法,利用ThreadPoolExecutor的API。3. 管理线程池实例,确保复用。4. 参数校验异常处理。5. 提供监控接口。6. 编写示例代码注意事项。</think>### Java线程池工具类设计与实现 #### 一、核心设计思路 1. **工厂模式封装**:通过工厂方法创建同类型的线程池(固定大小/缓存/定时任务等)[^3][^4] 2. **配置中心化**:使用Builder模式支持参数动态配置 3. **动态调整机制**:利用ThreadPoolExecutor的开放API实现运行时参数修改 4. **实例复用管理**:通过注册表管理已创建线程池实例 #### 二、工具类实现代码 ```java public class ThreadPoolManager { private static final Map<String, ThreadPoolExecutor> POOL_REGISTRY = new ConcurrentHashMap<>(); // 创建线程池并注册 public static ThreadPoolExecutor createPool(String poolName, int coreSize, int maxSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> queue) { return POOL_REGISTRY.computeIfAbsent(poolName, k -> new ThreadPoolExecutor(coreSize, maxSize, keepAliveTime, unit, queue)); } // 动态调整核心参数 public static void adjustCorePoolSize(String poolName, int newCoreSize) { ThreadPoolExecutor pool = POOL_REGISTRY.get(poolName); if (pool != null) { pool.setCorePoolSize(newCoreSize); pool.setMaximumPoolSize(Math.max(newCoreSize, pool.getMaximumPoolSize())); } } // 获取监控数据 public static PoolStats getPoolStats(String poolName) { ThreadPoolExecutor pool = POOL_REGISTRY.get(poolName); return new PoolStats( pool.getPoolSize(), pool.getActiveCount(), pool.getQueue().size() ); } // 统计数据封装类 public record PoolStats(int poolSize, int activeThreads, int queueSize) {} } ``` #### 三、关键实现细节 1. **线程池类型适配**: ```java // 创建固定大小线程池 ThreadPoolExecutor fixedPool = ThreadPoolManager.createPool("fixed-pool", 10, 10, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // 创建弹性线程池 ThreadPoolExecutor elasticPool = ThreadPoolManager.createPool("elastic-pool", 5, 20, 60, TimeUnit.SECONDS, new SynchronousQueue<>()); ``` 2. **动态参数调整**: ```java // 动态调整核心线程数 ThreadPoolManager.adjustCorePoolSize("order-service-pool", Runtime.getRuntime().availableProcessors() * 2); ``` 3. **监控集成**: ```java PoolStats stats = ThreadPoolManager.getPoolStats("payment-pool"); System.out.printf("当前活跃线程:%d,队列堆积:%d%n", stats.activeThreads(), stats.queueSize()); ``` #### 四、最佳实践建议 1. **队列选择策略**: - CPU密集型任务使用有界队列(`ArrayBlockingQueue`) - IO密集型任务使用无界队列(`LinkedBlockingQueue`) 2. **拒绝策略配置**: ```java new ThreadPoolExecutor(..., new ThreadPoolExecutor.CallerRunsPolicy()); ``` 3. **生命周期管理**: ```java // 优雅关闭线程池 public static void shutdownPool(String poolName) { ThreadPoolExecutor pool = POOL_REGISTRY.get(poolName); if (pool != null) { pool.shutdown(); try { if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); } } catch (InterruptedException e) { pool.shutdownNow(); } } } ``` #### 五、注意事项 1. **参数验证**:在设置参数时检查数值有效性(如核心线程能超过最大线程数) 2. **监控集成**:建议集成Micrometer等监控组件实现指标可视化 3. **命名规范**:使用有意义的线程池名称便于问题排查 4. **资源释放**:通过注册表管理确保使用线程池能被正确回收
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值