LinkedBlockQueue

本文详细分析了LinkedBlockQueue的源码,它是一个双端链表结构,采用线程安全的方式管理数据。offer()方法在队列未满时插入元素,put()方法在队列满时会阻塞。poll()和take()在队列不为空时取出元素,否则等待。这些方法确保了生产者消费者模型的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LinkedBlockQueue 源码分析

LinkedBlockQueue 继承关系

  • Collection
    • AbstractCollection
      • AbstractQueue
      • LinkedBlockingQueue

LinkedBlockQueue 还实现了 BlockingQueue 接口。 通过继承关系可以知道 LinkedBlockQueue 是一个容器类,所以就主要分析他的数据封装方式和对数据的管理方式。

BlockingQueue 接口的一些方法

博客围绕 LinkedBlockQueue 对 BlockingQueue 的方法的一些实现进行分析, 因为LinkedBlockQueue 继承自 AbstractQueue 且 AbstractQueue 中 add() 内部调用了 offer(), remove() 方法内部调用了 poll(),element() 调用了 peek(), 所以针对LinkedBlockQueue 主要分析这三个方法

public interface BlockingQueue<E> extends Queue<E> {
    
    boolean add(E e);

    boolean offer(E e);

    void put(E e) throws InterruptedException;

    boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException;

    E take() throws InterruptedException;

    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;

    boolean remove(Object o);

    boolean contains(Object o);
}

LinkedBlockQueue 源码分析

offer() 方法
public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    final AtomicInteger count = this.count;
    if (count.get() == capacity)
        return false;
    final int c;
    final Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        if (count.get() == capacity)
            return false;
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
    return true;
}

/**
 * Linked list node class.
 */
static class Node<E> {
    E item;
    /**
     * One of:
     * - the real successor Node
     * - this Node, meaning the successor is head.next
     * - null, meaning there is no successor (this is the last node)
     */
    Node<E> next;
    Node(E x) { item = x; }
}
  1. 如果LinkedBlockQueue 设置了 capacity 则先判断当前队列是否已满,可以在LinkedBlockQueue中设置容量大小,默认值是 Integer.MAX_VALUE.
  2. 数据会封装到Node节点中
  3. 获取 putLock 并执行入队操作
  4. 如果队列未满,则唤醒put等待线程(不一定唤醒,因为没有Condition.await())的情况下也会调用
  5. offer() 方法是线程安全的方法
/**
 * Links node at end of queue.
 *
 * @param node the node
 */
private void enqueue(Node<E> node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    last = last.next = node;
}

入队操作是设置当前 last.next = node 且 last 指向当前 Node. 所以 LinkedBlockQueue 是一个双端队列,first 指向队头, last 指向队尾。取的时候从队头取,插入时往队尾插入。

poll() 方法
public E poll() {
    final AtomicInteger count = this.count;
    if (count.get() == 0)
        return null;
    final E x;
    final int c;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        if (count.get() == 0)
            return null;
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}
  1. 当前队列为空时 poll() 返回 null
  2. 获取 takeLock, 通过上面 offer() 方法知道,LinkedBlockQueue 维护了两个 ReentrantLock, 两个维度同步队列的获取和插入操作
  3. dequeue() 从队头获取数据
/**
 * Removes a node from head of queue.
 *
 * @return the node
 */
private E dequeue() {
    // assert takeLock.isHeldByCurrentThread();
    // assert head.item == null;
    Node<E> h = head;
    Node<E> first = h.next;
    h.next = h; // help GC
    head = first;
    E x = first.item;
    first.item = null;
    return x;
}

上面的方法没有讲清楚 LinkedBlockQueue 的阻塞关系。所以需要看另外两个方法。

put() 方法
/**
 * Inserts the specified element at the tail of this queue, waiting if
 * necessary for space to become available.
 *
 * @throws InterruptedException {@inheritDoc}
 * @throws NullPointerException {@inheritDoc}
 */
public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    final int c;
    final Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    try {
        while (count.get() == capacity) {
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

这个方法比 offer() 多了一段代码, 就是当 count.get() == capacity 时,调用 notFull.await() 方法,就是说当队列满的时候要想再插入数据, 就需要等待队列取出数据。

take() 方法
public E take() throws InterruptedException {
    final E x;
    final int c;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

这个方法比 poll() 方法多了 count.get() == 0 时阻塞等待的过程。

总结
  1. LinkedBlockQueue 是一个双端链表的数据结构,队尾 last 插入数据,队头 head 取出数据
  2. LinkedBlockQueue 是线程安全的数据结构
  3. offer()和poll() 对应, put()和take() 对应且能满足生产者消费者模型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值