一、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, // corePoolSize5, // maximumPoolSize60, TimeUnit.SECONDS, // keepAliveTimenew 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 方便直接使用。