一句话说透Java里面的线程池的工作队列

166 阅读3分钟

一句话总结:
线程池的工作队列就像快递站的候客区——任务太多时先排队,队列类型不同决定了候客区是无限大(容易爆仓)还是容量固定(招临时工)!


一、工作队列的作用:任务缓冲区

当线程池的核心线程都在忙时,新任务会进入队列等待,直到有线程空闲或触发扩容。队列类型直接影响线程池的行为和性能


二、四种常见候客区(队列类型)

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 先,延迟队列定时发。
同步队列零缓冲,慎用临时工别抓瞎!”


六、实战技巧

  1. 监控队列大小

    ThreadPoolExecutor pool = ...;  
    int queueSize = pool.getQueue().size();  
    System.out.println("当前排队任务数:" + queueSize);  
    
  2. 动态调整队列容量(需自定义队列):

    public class ResizableCapacityQueue<T> extends LinkedBlockingQueue<T> {  
        public void setCapacity(int capacity) {  
            // 反射修改队列容量(慎用!)  
        }  
    }  
    
  3. 队列满时的优雅处理

    • 使用自定义拒绝策略,记录日志或降级处理:

    new ThreadPoolExecutor.CallerRunsPolicy() {  
        @Override  
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {  
            log.error("任务被拒绝,当前队列大小:" + executor.getQueue().size());  
            // 降级处理:存入数据库稍后重试  
        }  
    };  
    

总结

“队列选型看场景,无界有界要衡量。
高并发慎无界,定时任务延迟上。
优先任务插队走,拒绝策略保平安!”