JUC队列-LinkedBlockingDeque(三)

LinkedBlockingDeque介绍

LinkedBlockingDeque是双向链表实现的阻塞队列。该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(插入/删除);

此外,LinkedBlockingDeque还是可选容量的,防止过度膨胀,默认等于Integer.MAX_VALUE。

LinkedBlockingDeque的uml图

《JUC队列-LinkedBlockingDeque(三)》

说明:

  1. LinkedBlockingDeque继承于AbstractQueue,它本质上是一个支持FIFO和FILO的双向的队列。
  2. LinkedBlockingDeque实现了BlockingDeque接口,它支持多线程并发。当多线程竞争同一个资源时,某线程获取到该资源之后,其它线程需要阻塞等待。
  3. LinkedBlockingDeque是通过双向链表实现的。
    3.1 first是双向链表的表头。
    3.2 last是双向链表的表尾。
    3.3 count是LinkedBlockingDeque的实际大小,即双向链表中当前节点个数。
    3.4 capacity是LinkedBlockingDeque的容量,它是在创建LinkedBlockingDeque时指定的。
    3.5 lock是控制对LinkedBlockingDeque的互斥锁,当多个线程竞争同时访问LinkedBlockingDeque时,某线程获取到了互斥锁lock,其它线程则需要阻塞等待,直到该线程释放lock,其它线程才有机会获取lock从而获取cpu执行权。
    3.6 notEmpty和notFull分别是“非空条件”和“未满条件”。通过它们能够更加细腻进行并发控制。

LinkedBlockingDeque源码分析

构造方法

public LinkedBlockingDeque(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
}

其他成员的初始化:

// “双向队列”的表头
transient Node<E> first;
// “双向队列”的表尾
transient Node<E> last;
// 节点数量
private transient int count;
// 容量
private final int capacity;
// 互斥锁 , 互斥锁对应的“非空条件notEmpty”, 互斥锁对应的“未满条件notFull”
final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();

添加方法

  public void put(E e) throws InterruptedException {
        putLast(e);
    }

putLast方法

  public void putLast(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        //构造节点
        Node<E> node = new Node<E>(e);
        // 获得锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //如果插入失败,则等待直至插入成功。
            while (!linkLast(node))
                notFull.await();
        } finally {
            lock.unlock();
        }
    }

linkLast方法

private boolean linkLast(Node<E> node) {
    // 如果“双向链表的节点数量” > “容量”,则返回false,表示插入失败。
    if (count >= capacity)
        return false;
    // 将“node添加到链表末尾”,并设置node为新的尾节点
    Node<E> l = last;
    node.prev = l;
    last = node;
    if (first == null)
        first = node;
    else
        l.next = node;
    // 将“节点数量”+1
    ++count;
    // 插入节点之后,唤醒notEmpty上的等待线程。
    notEmpty.signal();
    return true;
}

如果要删除元素,则调用take方法,take方法跟put方法镜像,方法比较类似,就不展示出来啦。

LinkedBlockingDueue与LinkedBlockingQueue的区别:

LinkedBlockingDueue没有进行读写锁的分离,因此同一时间只能有一个线程对其操作,因此在高并发应用中,它的性能要远远低于LinkedBlockingQueue。

    原文作者:JUC
    原文地址: https://blog.csdn.net/qq_33394088/article/details/79058879
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞