一句话说透Java里面的LinkedBlockingQueue

253 阅读3分钟

LinkedBlockingQueue 就像一条  “允许双端同时操作的传送带” ,生产者在一端放东西,消费者在另一端取东西,两边可以同时工作互不干扰。它是 Java 中线程安全的高性能阻塞队列,专为高并发场景设计。


一、核心特点

  1. 基于链表实现

    • 元素存储在链表的节点中,可以动态扩展(默认无界,但建议设置容量避免内存耗尽)。
    • 示例new LinkedBlockingQueue<>(100); 表示最多容纳100个元素。
  2. 双锁机制

    • 生产者锁putLock):控制入队操作。
    • 消费者锁takeLock):控制出队操作。
    • 优势:生产者和消费者可以同时操作,互不阻塞,提高并发性能。
  3. 阻塞行为

    • 队列满时:生产者调用 put() 会被阻塞,直到有空位。
    • 队列空时:消费者调用 take() 会被阻塞,直到有新元素。
    • 非阻塞方法offer() 和 poll() 直接返回成功与否,不等待。

二、与 ArrayBlockingQueue 对比

对比项LinkedBlockingQueueArrayBlockingQueue
底层结构链表(动态扩展)数组(固定容量)
锁机制双锁(生产消费并行)单锁(生产消费互斥)
默认容量无界(Integer.MAX_VALUE必须指定容量
内存占用更高(每个元素多两个指针)更低(连续内存,无指针开销)
吞吐量更高(适合高并发)较低(单锁竞争)

三、适用场景

  1. 高并发生产者-消费者

    • 比如电商秒杀活动,大量用户同时下单(生产者)和系统处理订单(消费者)。
    BlockingQueue<Order> orderQueue = new LinkedBlockingQueue<>(1000);  
    
  2. 任务调度池

    • 线程池(如 Executors.newFixedThreadPool)默认使用无界 LinkedBlockingQueue

    ExecutorService executor = Executors.newFixedThreadPool(4);  
    // 内部任务队列:new LinkedBlockingQueue<>()  
    
  3. 流式数据处理

    • 数据流水线处理,如日志收集后批量写入数据库。

四、工作原理

  1. 入队(生产者)

    • 获取 putLock,检查队列是否已满:

      • 如果未满 → 插入新节点到链表尾部。
      • 如果已满 → 阻塞等待,直到消费者取走元素唤醒。
    • 释放 putLock,并通知消费者队列非空。

  2. 出队(消费者)

    • 获取 takeLock,检查队列是否为空:

      • 如果不空 → 移除链表头部节点并返回。
      • 如果空 → 阻塞等待,直到生产者放入元素唤醒。
    • 释放 takeLock,并通知生产者队列有空位。


五、代码示例

public class ProducerConsumerExample {
    public static void main(String[] args) {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(5);

        // 生产者
        new Thread(() -> {
            try {
                queue.put("任务1");
                queue.put("任务2");
                queue.put("任务3"); // 队列满时阻塞
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();

        // 消费者
        new Thread(() -> {
            try {
                String task = queue.take(); // 队列空时阻塞
                System.out.println("处理任务:" + task);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
    }
}

六、注意事项

  1. 避免无界队列

    • 默认无界队列可能导致内存溢出(OOM),务必设置合理容量。
  2. 平衡生产消费速度

    • 如果生产者远快于消费者,即使队列有界也可能积压,需监控队列大小。
  3. 优先用 offer() 和 poll()

    • 非阻塞方法更安全,避免线程长时间阻塞。

七、总结

LinkedBlockingQueue 是高并发场景的利器

  • 优势:双锁机制提升吞吐量,动态扩展更灵活。
  • 注意:内存开销略大,需合理设置容量。

适用场景

  • 高并发且生产消费操作频繁。
  • 需要灵活扩展队列容量(但需谨慎无界风险)。

口诀
「链表队列双锁强,生产消费两不忙
高并发下吞吐高,内存稍大也无妨
默认无界有隐患,设置容量保平安!」