LinkedBlockingDeque 源码分析

LinkedBlockingDeque

LinkedBlockingDeque 能解决什么问题?什么时候使用 LinkedBlockingDeque?

1)LinkedBlockingDeque 是基于双向链表实现的,可以选择有界或无界的双端阻塞队列。
2)LinkedBlockingDeque 使用相同的互斥锁来保证线程安全性,读写操作性能低于 LinkedBlockingQueue。

如何使用 LinkedBlockingDeque?

1)并发场景下,需要作为双端队列使用时,如果只是作为 FIFO 队列使用,则 LinkedBlockingQueue 的性能更高。
2)指定队列的容量,以避免生产速率远高于消费速率时资源耗尽的问题。

使用 LinkedBlockingDeque 有什么风险?

1)未指定容量的情况下,生产速率远高于消费速率时,会导致内存耗尽而 OOM。
2)高并发场景下,性能远低于 LinkedBlockingQueue。
3)由于需要维持前后节点的链接,内存消耗也高于 LinkedBlockingQueue。

LinkedBlockingDeque 核心操作的实现原理?

  • 创建实例
    /** 双向链表节点 */
    static final class Node<E> {
        /**
         *  节点元素,如果节点已经被移除,则为 null
         */
        E item;

        /**
         * One of:
         * - the real predecessor Node
         * - this Node, meaning the predecessor is tail
         * - null, meaning there is no predecessor
         */
        Node<E> prev;

        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head
         * - null, meaning there is no successor
         */
        Node<E> next;

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

    /**
     *  头结点
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     *  尾节点
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

    /** 双端队列中的元素总数 */
    private transient int count;

    /** 双端队列的容量 */
    private final int capacity;

    /** 控制访问的锁 */
    final ReentrantLock lock = new ReentrantLock();

    /** 队列为空时,用于阻塞执行 take 操作的线程的非空条件 */
    private final Condition notEmpty = lock.newCondition();

    /** 队列已满时,用于阻塞执行 put 操作的线程的非满条件  */
    private final Condition notFull = lock.newCondition();

    /**
     *  创建一个容量为 Integer.MAX_VALUE 的双端阻塞队列
     */
    public LinkedBlockingDeque() {
        this(Integer.MAX_VALUE);
    }

    /**
     *  创建一个容量为 capacity 的双端阻塞队列
     */
    public LinkedBlockingDeque(int capacity) {
        if (capacity <= 0) {
            throw new IllegalArgumentException();
        }
        this.capacity = capacity;
    }
  • 将目标元素 e 添加到队列头部,如果队列已满,则阻塞等待有可用空间后重试
    /**
     *  将目标元素 e 添加到队列头部,如果队列已满,则阻塞等待有可用空间后重试
     */
    public void putFirst(E e) throws InterruptedException {
        if (e == null) {
            throw new NullPointerException();
        }
        final Node<E> node = new Node<>(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // 尝试在头部添加元素
            while (!linkFirst(node)) {
                // 当前线程在非满条件上等待
                notFull.await();
            }
        } finally {
            lock.unlock();
        }
    }

    private boolean linkFirst(Node<E> node) {
        // 队列已满,则直接返回 false
        if (count >= capacity) {
            return false;
        }
        // 读取头节点
        final Node<E> f = first;
        // 将旧头结点链接到目标节点之后
        node.next = f;
        // 写入新头节点
        first = node;
        // 1)当前元素为第一个添加到队列中的元素
        if (last == null) {
            // 写入尾节点
            last = node;
        } else {
            // 将旧头节点的前置节点设置为新头结点
            f.prev = node;
        }
        // 递增计数
        ++count;
        // 唤醒在非空条件上阻塞等待的线程来读取元素
        notEmpty.signal();
        return true;
    }
  • 如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列头部
    /**
     *  如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列头部
     */
    @Override
    public boolean offerFirst(E e) {
        if (e == null) {
            throw new NullPointerException();
        }
        final Node<E> node = new Node<>(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return linkFirst(node);
        } finally {
            lock.unlock();
        }
    }
  • 在指定的超时时间内尝试将目标元素 e 添加到队列头部,成功则返回 true
    /**
     *  在指定的超时时间内尝试将目标元素 e 添加到队列头部,成功则返回 true
     */
    @Override
    public boolean offerFirst(E e, long timeout, TimeUnit unit)
            throws InterruptedException {
        if (e == null) {
            throw new NullPointerException();
        }
        final Node<E> node = new Node<>(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            // 头结点添加失败
            while (!linkFirst(node)) {
                // 已经超时则直接返回
                if (nanos <= 0L) {
                    return false;
                }
                // 当前线程在非满条件上阻塞等待,唤醒后再次尝试添加
                nanos = notFull.awaitNanos(nanos);
            }
            return true;
        } finally {
            lock.unlock();
        }
    }
  • 将目标元素 e 添加到队列尾部,如果队列已满,则阻塞等待有可用空间后重试
    /**
     *  将目标元素 e 添加到队列尾部,如果队列已满,则阻塞等待有可用空间后重试    
     */
    @Override
    public void putLast(E e) throws InterruptedException {
        if (e == null) {
            throw new NullPointerException();
        }
        final Node<E> node = new Node<>(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // 尝试将节点链接到队列尾部
            while (!linkLast(node)) {
                // 队列已满,当前线程在非满条件上阻塞等待,被唤醒后再次尝试
                notFull.await();
            }
        } finally {
            lock.unlock();
        }
    }

    private boolean linkLast(Node<E> node) {
        // 队列已满,则直接返回 false
        if (count >= capacity) {
            return false;
        }
        // 读取尾节点
        final Node<E> l = last;
        // 将目标节点链接到尾节点之后
        node.prev = l;
        // 写入尾节点为新增节点
        last = node;
        // 1)当前元素是第一个加入队列的元素
        if (first == null) {
            // 写入头结点
            first = node;
        } else {
            // 将旧尾节点的后置节点更新为新增节点
            l.next = node;
        }
        // 递增总数
        ++count;
        // 唤醒在非空条件上等待的线程
        notEmpty.signal();
        return true;
    }
  • 如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列尾部
    /**
     *  如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列尾部
     */
    @Override
    public boolean offerLast(E e) {
        if (e == null) {
            throw new NullPointerException();
        }
        final Node<E> node = new Node<>(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return linkLast(node);
        } finally {
            lock.unlock();
        }
    }
  • 在指定的超时时间内尝试将目标元素 e 添加到队列尾部,成功则返回 true
    /**
     *  在指定的超时时间内尝试将目标元素 e 添加到队列尾部,成功则返回 true
     */
    @Override
    public boolean offerLast(E e, long timeout, TimeUnit unit)
            throws InterruptedException {
        if (e == null) {
            throw new NullPointerException();
        }
        final Node<E> node = new Node<>(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            // 尝试将目标元素 e 添加到队列尾部
            while (!linkLast(node)) {
                // 已经超时则直接返回 false
                if (nanos <= 0L) {
                    return false;
                }
                // 当前线程在非满条件上阻塞等待,被唤醒后再次尝试
                nanos = notFull.awaitNanos(nanos);
            }
            return true;
        } finally {
            lock.unlock();
        }
    }
  • 移除并返回头部节点,如果队列为空,则阻塞等待有可用元素之后重试
    /**
     *  移除并返回头部节点,如果队列为空,则阻塞等待有可用元素之后重试
     * created by ZXD at 6 Dec 2018 T 21:00:25
     * @return
     * @throws InterruptedException
     */
    @Override
    public E takeFirst() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E x;
            // 尝试移除并返回头部节点
            while ( (x = unlinkFirst()) == null) {
                // 队列为空,则阻塞等待有可用元素之后重试
                notEmpty.await();
            }
            return x;
        } finally {
            lock.unlock();
        }
    }
  • 移除并返回尾部节点,如果队列为空,则阻塞等待有可用元素之后重试
    /**
     *  移除并返回尾部节点,如果队列为空,则阻塞等待有可用元素之后重试
     * created by ZXD at 6 Dec 2018 T 21:02:04
     * @return
     * @throws InterruptedException
     */
    @Override
    public E takeLast() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            E x;
            // 尝试移除并返回尾部节点
            while ( (x = unlinkLast()) == null) {
                // 队列为空,则阻塞等待有可用元素之后重试
                notEmpty.await();
            }
            return x;
        } finally {
            lock.unlock();
        }
    }
  • 如果队列为空,则立即返回 null,否则移除并返回头部元素
    /**
     *  如果队列为空,则立即返回 null,否则移除并返回头部元素
     * created by ZXD at 6 Dec 2018 T 21:03:40
     * @return
     */
    @Override
    public E pollFirst() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return unlinkFirst();
        } finally {
            lock.unlock();
        }
    }
  • 如果队列为空,则立即返回 null,否则移除并返回尾部元素
    /**
     *  如果队列为空,则立即返回 null,否则移除并返回尾部元素
     * created by ZXD at 6 Dec 2018 T 21:04:43
     * @return
     */
    @Override
    public E pollLast() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return unlinkLast();
        } finally {
            lock.unlock();
        }
    }
  • 在指定的超时时间内尝试移除并返回头部元素,如果已经超时,则返回 null
    /**
     *  在指定的超时时间内尝试移除并返回头部元素,如果已经超时,则返回 null
     * created by ZXD at 6 Dec 2018 T 21:05:21
     * @param timeout
     * @param unit
     * @return
     * @throws InterruptedException
     */
    @Override
    public E pollFirst(long timeout, TimeUnit unit)
            throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            E x;
            // 尝试移除并返回头部元素
            while ( (x = unlinkFirst()) == null) {
                // 已经超时则返回 null
                if (nanos <= 0L) {
                    return null;
                }
                // 当前线程在非空条件上阻塞等待,被唤醒后进行重试
                nanos = notEmpty.awaitNanos(nanos);
            }
            // 移除成功则直接返回头部元素
            return x;
        } finally {
            lock.unlock();
        }
    }
  • 在指定的超时时间内尝试移除并返回尾部元素,如果已经超时,则返回 null
    /**
     * created by ZXD at 6 Dec 2018 T 21:08:24
     * @param timeout
     * @param unit
     * @return
     * @throws InterruptedException
     */
    @Override
    public E pollLast(long timeout, TimeUnit unit)
            throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            E x;
             // 尝试移除并返回尾部元素
            while ( (x = unlinkLast()) == null) {
                // 已经超时则返回 null
                if (nanos <= 0L) {
                    return null;
                }
                // 当前线程在非空条件上阻塞等待,被唤醒后进行重试
                nanos = notEmpty.awaitNanos(nanos);
            }
            // 移除成功则直接返回尾部元素
            return x;
        } finally {
            lock.unlock();
        }
    }

posted on 2018-12-06 21:03  竺旭东  阅读(246)  评论(0编辑  收藏  举报

导航