莫方教程网

专业程序员编程教程与实战案例分享

Java阻塞队列:LinkedBlockingQueue

LinkedBlockingQueue

LinkedBlockingQueue 是 Java 并发包中基于链表实现的线程安全阻塞队列,适用于生产者-消费者模式中的任务队列实现。

一、核心特性

数据结构

  • 基于单向链表的 FIFO 队列实现,内部通过 head 和 last 节点维护队列结构;
  • 默认容量为 Integer.MAX_VALUE(理论无界),但可手动指定固定容量实现有界队列。
  • 内部使用单向链表来存储元素,这使得它在内存使用上相对灵活,因为它的容量可以是有限的也可以是无限的。

并发控制

  • 使用分离锁机制:putLock 控制入队操作,takeLock 控制出队操作,提升并发吞吐量;
  • 默认采用非公平锁策略,初始化时可指定公平锁(但可能降低性能)。

阻塞策略

  • 队列满时,入队操作阻塞(put() 方法)或超时返回(offer(e, timeout, unit));
  • 队列空时,出队操作阻塞(take() 方法)或超时返回(poll(timeout, unit))。
  • 当尝试从空队列中获取元素时,获取操作会被阻塞直到队列变得非空;同样地,如果试图将元素放入已满的有界队列中,放入操作也会被阻塞直到空间可用。

二、常用方法对比

操作类型

抛出异常

返回特殊值

阻塞

超时阻塞

插入元素

add(e)

offer(e)

put(e)

offer(e, t, u)

移除元素

remove()

poll()

take()

poll(t, u)

检查元素

element()

peek()

-

-

  • 队列满时:add() 抛 IllegalStateException,offer() 返回 false,put() 无限阻塞;
  • 队列空时:remove() 抛 NoSuchElementException,poll() 返回 null,take() 无限阻塞。

三、源码实现要点

入队逻辑

  • 通过 enqueue(node) 方法将新节点链接至链表尾部,更新 last 指针,并通过 count.getAndIncrement() 原子更新队列长度;
  • 若插入后队列未满,通过 signalNotEmpty() 唤醒可能阻塞的消费者线程。

出队逻辑

  • 通过 dequeue() 方法移除链表头节点,更新 head 指针,并通过 count.getAndDecrement() 原子减少队列长度;
  • 若移除后队列非空,通过 signalNotFull() 唤醒可能阻塞的生产者线程。

四、适用场景

高并发生产者-消费者模型

  • 分离锁设计减少锁竞争,适合生产消费速率差异大的场景;
  • 默认无界队列适用于任务量不可预知但需快速响应的场景,但需警惕内存溢出风险。
  • LinkedBlockingQueue 经常被用作工作队列,在执行器框架(如 ThreadPoolExecutor)中作为任务提交和执行之间的桥梁。

性能对比

  • 相比 ArrayBlockingQueue,链表结构更灵活,但连续内存访问效率稍低;
  • 相比 PriorityBlockingQueue,严格保证 FIFO 顺序,不支持优先级排序。

五、典型示例

BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(100); // 有界队列,容量100
// 生产者线程
new Thread(() -> {
    try {
        queue.put(1); // 阻塞插入
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
}
}).start();
// 消费者线程
new Thread(() -> {
    try {
        Integer value = queue.take(); // 阻塞获取
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}).start();
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言

    滇ICP备2024046894号-1