一句话总结:
线程池的工作队列就像快递站的候客区——任务太多时先排队,队列类型不同决定了候客区是无限大(容易爆仓)还是容量固定(招临时工)!
一、工作队列的作用:任务缓冲区
当线程池的核心线程都在忙时,新任务会进入队列等待,直到有线程空闲或触发扩容。队列类型直接影响线程池的行为和性能!
二、四种常见候客区(队列类型)
1. 无限候客区(LinkedBlockingQueue)
-
特点:
- 容量无限(实际受内存限制),任务一直堆积。
- 默认使用在
FixedThreadPool和SingleThreadExecutor。
-
风险:
- 任务暴增 → 队列撑爆内存 → OOM 崩溃!
-
代码示例:
new ThreadPoolExecutor( 5, 5, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>() // 默认容量 Integer.MAX_VALUE(约21亿) );
2. 固定座位候客区(ArrayBlockingQueue)
-
特点:
- 容量固定(需手动设置),任务超出容量时触发扩容(招临时工)。
- 公平性:可设置是否公平(先到先得 vs 插队抢座)。
-
适用场景:
- 需要控制队列长度的场景,避免内存溢出。
-
代码示例:
new ThreadPoolExecutor( 5, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100) // 容量100 );
3. 无候客区(SynchronousQueue)
-
特点:
- 容量为0!来一个任务必须立刻处理,否则触发扩容(招临时工)。
- 默认使用在
CachedThreadPool。
-
风险:
- 任务暴增 → 线程数爆炸 → CPU 100% !
-
代码示例:
new ThreadPoolExecutor( 0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<>() );
4. 优先通道(PriorityBlockingQueue)
-
特点:
- 任务按优先级排序(需实现
Comparable接口)。 - 容量无限,但可动态调整优先级。
- 任务按优先级排序(需实现
-
适用场景:
- 需要优先处理 VIP 任务(如支付订单优先于普通请求)。
-
代码示例:
new ThreadPoolExecutor( 5, 10, 60, TimeUnit.SECONDS, new PriorityBlockingQueue<>() );
三、特殊候客区(DelayedWorkQueue)
-
专属场景:
- 用于
ScheduledThreadPool,任务按延迟时间排序。
- 用于
-
特点:
- 内部基于堆结构实现,确保延迟最短的任务先执行。
-
代码示例:
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3); pool.schedule(() -> System.out.println("3秒后执行"), 3, TimeUnit.SECONDS);
四、选型指南:根据场景选队列
| 场景 | 推荐队列类型 | 原因 |
|---|---|---|
| 高并发可控任务量 | ArrayBlockingQueue | 防止内存溢出,队列满触发扩容 |
| 严格顺序执行 | LinkedBlockingQueue | 保证任务先进先出 |
| 瞬时高吞吐 | SynchronousQueue | 来一个处理一个,快速响应 |
| 优先级任务 | PriorityBlockingQueue | 按优先级处理任务 |
| 定时/延迟任务 | DelayedWorkQueue | 按延迟时间排序执行 |
五、避坑口诀
“无界队列是炸弹,任务堆积会爆炸。
优先队列 VIP 先,延迟队列定时发。
同步队列零缓冲,慎用临时工别抓瞎!”
六、实战技巧
-
监控队列大小:
ThreadPoolExecutor pool = ...; int queueSize = pool.getQueue().size(); System.out.println("当前排队任务数:" + queueSize); -
动态调整队列容量(需自定义队列):
public class ResizableCapacityQueue<T> extends LinkedBlockingQueue<T> { public void setCapacity(int capacity) { // 反射修改队列容量(慎用!) } } -
队列满时的优雅处理:
-
使用自定义拒绝策略,记录日志或降级处理:
new ThreadPoolExecutor.CallerRunsPolicy() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { log.error("任务被拒绝,当前队列大小:" + executor.getQueue().size()); // 降级处理:存入数据库稍后重试 } }; -
总结
“队列选型看场景,无界有界要衡量。
高并发慎无界,定时任务延迟上。
优先任务插队走,拒绝策略保平安!”