LinkedBlockingQueue

https://www.jianshu.com/p/cc2281b1a6bc

https://blog.csdn.net/tonywu1992/article/details/83419448

继承关系图

 

 

 

 

/**
     * 节点类,用于存储数据
     */
    static class Node<E> {
        E item;
        Node<E> next;

        Node(E x) { item = x; }
    }

    /**
     * 阻塞队列的大小,默认为Integer.MAX_VALUE
     */
    private final int capacity;

    /**
     * 当前阻塞队列中的元素个数
     */
    private final AtomicInteger count = new AtomicInteger();

    /**
     * 阻塞队列的头结点
     */
    transient Node<E> head;

    /**
     * 阻塞队列的尾节点
     */
    private transient Node<E> last;

    /**
     * 获取并移除元素时使用的锁,如take, poll, etc
     */
    private final ReentrantLock takeLock = new ReentrantLock();

    /**
     * notEmpty条件对象,当队列没有数据时用于挂起执行删除的线程 
     */
    private final Condition notEmpty = takeLock.newCondition();

    /**
     * 添加元素时使用的锁如 put, offer, etc
     */
    private final ReentrantLock putLock = new ReentrantLock();

    /**
     * notFull条件对象,当队列数据已满时用于挂起执行添加的线程
     */
    private final Condition notFull = putLock.newCondition();

这里如果不指定队列的容量大小,也就是使用默认的Integer.MAX_VALUE,如果存在添加速度大于删除速度时候,有可能会内存溢出,这点在使用前希望慎重考虑。

另外,LinkedBlockingQueue对每一个lock锁都提供了一个Condition用来挂起和唤醒其他线程。

默认的构造函数和最后一个构造函数创建的队列大小都为Integer.MAX_VALUE,只有第二个构造函数用户可以指定队列的大小。第二个构造函数最后初始化了last和head节点,让它们都指向了一个元素为null的节点。
在这里插入图片描述
最后一个构造函数使用了putLock来进行加锁,但是这里并不是为了多线程的竞争而加锁,只是为了放入的元素能立即对其他线程可见。

put方法来看,它总共做了以下情况的考虑:

队列已满,阻塞等待。
队列未满,创建一个node节点放入队列中,如果放完以后队列还有剩余空间,继续唤醒下一个添加线程进行添加。如果放之前队列中没有元素,放完以后要唤醒消费线程进行消费。

 

 链表算法

 

 

private E dequeue() {
    // 获取到head节点
    Node<E> h = head;
    // 获取到head节点指向的下一个节点,也就是节点A
    Node<E> first = h.next;
    // 获取到下下个节点,也就是节点B
    Node<E> next = first.next;
    // head的next指向下下个节点,也就是图中的B节点
    h.next = next;
    // 得到节点A的值
    E x = first.item;
    first.item = null; // help GC
    first.next = first; // help GC
    return x;
}

 

posted @ 2022-06-06 10:11  VipSoft  阅读(26)  评论(0编辑  收藏  举报