写点什么

ThreadPoolExecutor 详解

作者:秃头小帅oi
  • 2025-02-13
    福建
  • 本文字数:2316 字

    阅读完需:约 8 分钟

ThreadPoolExecutor 详解

一、ThreadPoolExecutor 核心参数

构造函数如下:

public ThreadPoolExecutor(    int corePoolSize,      // 核心线程数    int maximumPoolSize,   // 最大线程数    long keepAliveTime,    // 非核心线程空闲存活时间    TimeUnit unit,         // 存活时间单位    BlockingQueue<Runnable> workQueue, // 任务队列    RejectedExecutionHandler handler   // 拒绝策略)
复制代码

参数解析

1、corePoolSize

核心线程数:线程池中始终保持存活的线程数量(即使空闲)。默认情况下,核心线程不会自动销毁(除非设置 allowCoreThreadTimeOut(true))。
复制代码

2、maximumPoolSize

最大线程数:线程池允许创建的最大线程数量。当任务队列已满且当前线程数小于 maximumPoolSize 时,会创建新线程。
复制代码

3、keepAliveTime

非核心线程的空闲存活时间:当线程数超过 corePoolSize 时,空闲线程在销毁前的等待时间。
复制代码

4、workQueue 任务队列:

用于存储等待执行的任务。常用队列类型:LinkedBlockingQueue:无界队列(默认容量为 Integer.MAX_VALUE)。ArrayBlockingQueue:有界队列(需指定容量)。SynchronousQueue:直接传递队列(不存储任务,任务直接交给线程处理)。                            
复制代码

5、handler

拒绝策略:当任务队列已满且线程数达到 maximumPoolSize 时,处理新提交的任务。内置策略:    AbortPolicy(默认):抛出 RejectedExecutionException。    CallerRunsPolicy:由提交任务的线程直接执行该任务。    DiscardPolicy:静默丢弃任务。    DiscardOldestPolicy:丢弃队列中最旧的任务,然后重新提交当前任务。
复制代码

二、任务执行流程

1. 提交任务后,若当前线程数 < corePoolSize,则创建新线程执行任务。2.若线程数 ≥ corePoolSize,任务会被放入任务队列。3.若队列已满且线程数 < maximumPoolSize,则创建新线程执行任务。4.若队列已满且线程数 ≥ maximumPoolSize,触发拒绝策略。
复制代码

三、代码示例

// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(    2,                        // corePoolSize    5,                        // maximumPoolSize    60, TimeUnit.SECONDS,     // keepAliveTime    new ArrayBlockingQueue<>(10), // 容量为 10 的有界队列    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略);
// 提交任务for (int i = 0; i < 20; i++) { executor.execute(() -> { System.out.println("Task executed by " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } });}
// 关闭线程池executor.shutdown();
复制代码

四、常见面试题

1. 线程池的工作流程是什么?

核心线程是否已满 → 任务是否入队 → 队列是否已满 → 是否创建非核心线程 → 是否触发拒绝策略。
复制代码

2. corePoolSize 和 maximumPoolSize 的区别?

corePoolSize 是常驻线程数,maximumPoolSize 是线程池的容量上限。
复制代码

3. 为什么推荐手动创建线程池,而不是用 Executors 的工厂方法?

Executors 提供的默认线程池(如 newFixedThreadPool、newCachedThreadPool)可能隐藏风险:newFixedThreadPool 使用无界队列,可能堆积大量任务导致 OOM。newCachedThreadPool 允许无限创建线程,可能导致线程数爆炸。
复制代码

4. 任务队列的选择对线程池的影响?

无界队列(如 LinkedBlockingQueue):可能导致任务堆积,最终 OOM。有界队列(如 ArrayBlockingQueue):需合理设置队列容量,避免频繁触发拒绝策略。直接传递队列(SynchronousQueue):任务不存储,直接交给线程处理,适合高吞吐场景。
复制代码

5. 拒绝策略有哪些?实际项目中如何选择?

AbortPolicy:适合严格要求任务完整性的场景(如支付交易)。CallerRunsPolicy:适合允许任务延迟但必须完成的场景(如日志记录)。        DiscardOldestPolicy:适合允许丢弃旧任务的场景(如实时数据流)。
复制代码

6. 如何合理设置线程池参数?

CPU 密集型任务:corePoolSize = CPU 核心数 + 1(避免过多线程竞争 CPU)。IO 密集型任务:corePoolSize = 2 * CPU 核心数(充分利用线程等待 IO 的时间)。
复制代码

7. 线程池的关闭方法 shutdown() 和 shutdownNow() 的区别?

shutdown():平缓关闭,不再接受新任务,但会执行完队列中的任务。shutdownNow():立即关闭,尝试中断正在执行的任务,并清空队列。
复制代码

8. 如何监控线程池状态?

通过 ThreadPoolExecutor 提供的方法:getActiveCount():当前活动线程数。getQueue().size():队列中等待的任务数。getCompletedTaskCount():已完成的任务数。
复制代码

五、使用注意事项

避免无界队列:防止任务堆积导致内存溢出。
合理设置线程存活时间:避免频繁创建/销毁线程的开销。
自定义拒绝策略:根据业务需求处理无法执行的任务。
关闭线程池:确保程序退出时释放资源。
复制代码

六、corePoolSize、maximumPoolSize、workQueue 关系代码演示

七、拒绝策略代码演示


作为程序员,持续学习和充电非常重要,作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。低代码也是一个值得我们深入探索的领域,让我们拭目以待,它将给前端世界带来怎样的变革,推荐一个低代码工具。

应用地址:https://www.jnpfsoft.com


开发语言:Java/.net

这是一个基于 Flowable 引擎(支持 java、.NET),已支持 MySQL、SqlServer、Oracle、PostgreSQL、DM(达梦)、 KingbaseES(人大金仓)6 个数据库,支持私有化部署,前后端封装了上千个常用类,方便扩展,框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用。

用户头像

摸个鱼,顺便发点有用的东西 2023-06-19 加入

互联网某厂人(重生版)

评论

发布
暂无评论
ThreadPoolExecutor 详解_秃头小帅oi_InfoQ写作社区