线程池创建线程
文章目录
- 1. 可扩展线程池(`newCachedThreadPool`)
- 2. 固定线程池(`newFixedThreadPool`)
- 3. 单一线程池(`newSingleThreadExecutor`)
- 4. 延时周期线程池(`newScheduledThreadPool`)
- 1)特殊情况处理:
- 2)与 `scheduleWithFixedDelay` 的区别:
- 5. 自定义线程池(`ThreadPoolExecutor`)
- 1)`AbortPolicy`(默认策略)
- 2) `CallerRunsPolicy`
- 3) `DiscardPolicy`
- 4) `DiscardOldestPolicy`
- 5)拒绝策略选择建议
- 6)参数配置原则
- 6.核心区别对比表
- 7.注意事项
Java 中常见的线程池类型包括可扩展线程池、固定线程池、单一线程池、延时周期线程池以及自定义线程池,它们各自有不同的特性和适用场景,具体区别如下:
1. 可扩展线程池(newCachedThreadPool
)
- 核心特性:
- 线程数量动态变化,没有核心线程,最大线程数为
Integer.MAX_VALUE
(理论上可无限扩展) - 空闲线程超过60秒会自动销毁,避免资源浪费
- 使用
SynchronousQueue
作为任务队列(不存储任务,直接传递给线程)
- 线程数量动态变化,没有核心线程,最大线程数为
- 适用场景:
- 短期任务多、任务执行时间短的场景(如网络请求处理)
- 不需要控制线程数量上限的场景
- 示例:
ExecutorService cachedPool = Executors.newCachedThreadPool();
2. 固定线程池(newFixedThreadPool
)
- 核心特性:
- 线程数量固定(创建时指定,核心线程数 = 最大线程数)
- 线程不会自动销毁,即使空闲也会保留
- 使用
LinkedBlockingQueue
作为任务队列(无界队列,可存储大量等待任务)
- 适用场景:
- 任务数量稳定、执行时间较长的场景(如后台计算)
- 需要控制并发线程数,避免资源耗尽的场景
- 示例:
ExecutorService fixedPool = Executors.newFixedThreadPool(5); // 固定5个线程
3. 单一线程池(newSingleThreadExecutor
)
- 核心特性:
- 仅包含1个核心线程,所有任务按顺序执行
- 任务队列使用
LinkedBlockingQueue
(无界队列) - 线程意外终止时会自动创建新线程替代,保证始终有1个线程执行任务
- 适用场景:
- 需要任务按顺序执行的场景(如日志写入、单线程事务处理)
- 避免并发处理导致的数据不一致问题
- 示例:
ExecutorService singlePool = Executors.newSingleThreadExecutor();
4. 延时周期线程池(newScheduledThreadPool
)
- 核心特性:
- 可延迟执行任务或按周期重复执行任务
- 核心线程数固定,最大线程数为
Integer.MAX_VALUE
- 使用
DelayedWorkQueue
作为任务队列(按延迟时间排序)
- 适用场景:
- 定时任务(如定时备份、定时检查)
- 周期性任务(如每隔1小时执行一次统计)
- 示例:
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3); // 延迟2秒执行任务 scheduledPool.schedule(() -> System.out.println("延迟执行"), 2, TimeUnit.SECONDS); // 延迟1秒后,每3秒执行一次任务 scheduledPool.scheduleAtFixedRate(() -> System.out.println("周期执行"), 1, 3, TimeUnit.SECONDS);
1)特殊情况处理:
-
任务执行时间 > 周期:
- 下一次执行会在当前任务结束后立即开始(不会并行执行)
- 示例:若任务耗时4秒,周期3秒,则实际间隔为4秒
-
任务被中断:
- 若任务执行中被中断,后续周期执行会被取消
-
线程池关闭:
- 调用
shutdown()
后,已提交的周期任务会继续执行,直到所有任务完成或被中断
- 调用
2)与 scheduleWithFixedDelay
的区别:
scheduleAtFixedRate
:以上一次开始时间计算下一次执行时间(固定频率)scheduleWithFixedDelay
:以上一次结束时间计算下一次执行时间(固定延迟)
例如同样是3秒周期,任务耗时2秒:
scheduleAtFixedRate
:实际间隔为3秒(1→4→7秒执行)scheduleWithFixedDelay
:实际间隔为3+2=5秒(1→6→11秒执行)
5. 自定义线程池(ThreadPoolExecutor
)
- 核心特性:
- 通过
ThreadPoolExecutor
构造函数手动配置所有参数,灵活度最高 - 可自定义核心线程数、最大线程数、空闲线程存活时间、任务队列、拒绝策略等
- 通过
- 核心参数:
new ThreadPoolExecutor(corePoolSize, // 核心线程数(始终保留的线程数)maximumPoolSize, // 最大线程数(允许的最大线程数)keepAliveTime, // 非核心线程空闲超时时间unit, // 超时时间单位workQueue, // 任务队列(存储等待执行的任务)threadFactory, // 线程工厂(自定义线程名称、优先级等)handler // 拒绝策略(任务过多时的处理方式) );
- 适用场景:
- 对线程池有精细控制需求的场景(如自定义任务队列大小、拒绝策略)
- 复杂业务场景(如需要限制任务队列长度,避免内存溢出)
- 示例:
// 自定义线程池:核心2个,最大5个,队列长度10,拒绝策略为丢弃任务并记录日志 ThreadPoolExecutor customPool = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy() );
ThreadPoolExecutor
是 Java 中最核心的线程池实现类,通过灵活配置参数可满足不同场景需求。以下是其核心参数解释及四种内置拒绝策略的详细说明:
1)AbortPolicy
(默认策略)
- 行为:直接抛出
RejectedExecutionException
异常,阻止系统正常运行 - 适用场景:需要明确知道任务被拒绝的场景,便于及时处理
new ThreadPoolExecutor.AbortPolicy()
2) CallerRunsPolicy
- 行为:让提交任务的线程(调用者线程)亲自执行该任务
- 效果:减缓新任务提交速度,给线程池缓冲时间,避免任务丢失
- 适用场景:并发量不大、任务不紧急的场景
new ThreadPoolExecutor.CallerRunsPolicy()
3) DiscardPolicy
- 行为:直接丢弃新提交的任务,不抛出异常,也不做任何处理
- 风险:任务丢失,且无法感知
- 适用场景:任务无关紧要,允许丢失的场景(如日志收集)
new ThreadPoolExecutor.DiscardPolicy()
4) DiscardOldestPolicy
- 行为:丢弃等待队列中最旧的任务(队列头部任务),然后尝试提交新任务
- 适用场景:需要处理最新任务,旧任务可被替代的场景
new ThreadPoolExecutor.DiscardOldestPolicy()
5)拒绝策略选择建议
- 生产环境中不建议使用默认的 AbortPolicy,可能导致业务中断
- 对任务安全性要求高 → 选择
CallerRunsPolicy
(至少尝试执行) - 允许任务丢失但需最新任务 → 选择
DiscardOldestPolicy
- 非核心任务(如统计、日志)→ 可选择
DiscardPolicy
- 复杂场景可自定义拒绝策略(实现
RejectedExecutionHandler
接口),例如:// 自定义策略:记录被拒绝的任务,后续手动处理 RejectedExecutionHandler customHandler = (r, executor) -> {log.error("任务被拒绝: {}", r.toString());// 可将任务存入数据库或消息队列 };
6)参数配置原则
- 核心线程数:根据 CPU 核心数或业务并发量设置(如 CPU 密集型任务 = CPU 核心数 + 1)
- 最大线程数:避免过大(防止资源耗尽),通常为核心线程数的 2~4 倍
- 等待队列:建议使用有界队列(如
ArrayBlockingQueue
),避免无界队列导致 OOM - 拒绝策略:结合业务对任务丢失的容忍度选择,优先保证核心任务执行
6.核心区别对比表
线程池类型 | 核心线程数 | 最大线程数 | 任务队列类型 | 适用场景 | 特点总结 |
---|---|---|---|---|---|
可扩展线程池 | 0 | Integer.MAX_VALUE | SynchronousQueue | 短期、快速任务 | 动态扩缩容,自动销毁空闲线程 |
固定线程池 | n | n | LinkedBlockingQueue | 任务数量稳定、耗时较长 | 线程数量固定,复用性好 |
单一线程池 | 1 | 1 | LinkedBlockingQueue | 顺序执行任务 | 串行执行,保证任务顺序 |
延时周期线程池 | n | Integer.MAX_VALUE | DelayedWorkQueue | 定时/周期性任务 | 支持延迟和周期执行 |
自定义线程池 | 自定义 | 自定义 | 自定义(如ArrayBlockingQueue) | 复杂场景,需精细控制 | 灵活配置所有参数 |
7.注意事项
- 所有线程池使用后必须调用
shutdown()
或shutdownNow()
关闭,避免资源泄漏。
- 但是在延时周期任务中
scheduledAtFixedRate()
,添加了pool.shutdown()方法后,线程池会拒绝接收新任务。
Executors
提供的默认线程池可能存在隐患(如newFixedThreadPool
使用无界队列可能导致OOM),复杂场景建议使用自定义线程池- 选择线程池时需根据任务特性(执行时间、数量、并发需求)合理配置参数