Java并发集合之LinkedBlockingDeque使用与原理,附源码解析

LinkedBlockingDeque 简介

LinkedBlockingDeque是一个线程安全的双向并发阻塞队列,同时支持FIFO(先进先出)和FILO(先进后出)两种模式,并且为防止数据无限膨胀,可以设置阻塞队列的容量,默认不设置的话容量大小为Integer.MAX_VALUE

LinkedBlockingDeque 特性

  • 继承AbstractQueue,是一个同时支持FIFO和FILO的双向队列
  • 线程安全,多线程竞争时会阻塞等待,通过ReentrantLock实现
  • 通过双向链表实现,可以从头尾两端插入和读取数据,并且支持设置链表容量
  • 因为支持头尾双向的读写操作,因此读写锁是互斥的

LinkedBlockingDeque 数据结构

    // 双向链表头结点
    transient Node<E> first;

    // 双向链表尾节点
    transient Node<E> last;

    // 当前队列的数据总数
    private transient int count;

    // 队列的容量
    private final int capacity;

    // 读写锁
    final ReentrantLock lock = new ReentrantLock();

    // 读取数据线程等待条件
    private final Condition notEmpty = lock.newCondition();

    // 写入数据线程等待条件
    private final Condition notFull = lock.newCondition();

Node类定义

static final class Node<E> {
        // 链表元素
        E item;

        // 当前节点的前一个节点
        Node<E> prev;

        // 当前节点的后一个节点
        Node<E> next;

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

LinkedBlockingDeque 部分源码解析

构造方法

只是设置一下容量,并无过多操作

public LinkedBlockingDeque() {
        this(Integer.MAX_VALUE);
    }

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

变更方法

  • putFirst方法,阻塞式向队列头插入一条数据
public void putFirst(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 (!linkFirst(node))
                notFull.await();
        } finally {
            lock.unlock();
        }
    }
  • linkFirst 方法,将元素添加到队列头
private boolean linkFirst(Node<E> node) {
        // 队列当前元素数大于等于队列容量直接返回false
        if (count >= capacity)
            return false;
        // 获取头节点的引用
        Node<E> f = first;
        // 将当前节点的后节点设置为旧的头节点
        node.next = f;
        // 将头结点的引用指向当前节点
        first = node;
        // 尾节点为null表示队列只有一个元素,头尾节点指向当前节点
        if (last == null)
            last = node;
        else
        	// 设置旧的头结点前一个节点为当前节点
            f.prev = node;
        // 队列元素数量加一
        ++count;
        // 唤醒正在阻塞的读取队列的线程
        notEmpty.signal();
        return true;
    }
  • 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<E> l = last;
        // 设置当前节点的前一节点为原有的尾节点
        node.prev = l;
        // 设置尾节点引用为当前节点
        last = node;
        // 如果头节点为空,则表示当前节点为第一个节点,这只头节点和尾节点指向同一节点
        if (first == null)
            first = node;
        else
        	// 设置原有尾节点的下一节点为当前节点
            l.next = node;
        // 当前队列数量加一
        ++count;
        // 唤醒正在阻塞的读取队列的线程
        notEmpty.signal();
        return true;
    }
  • takeFirst 阻塞式从队列头获取元素
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();
        }
    }
  • unlinkFirst 方法
private E unlinkFirst() {
        // 获取双向链表头结点,如果为空直接返回
        Node<E> f = first;
        if (f == null)
            return null;
        // 获取头节点的下一节点,做新的头结点
        Node<E> n = f.next;
        // 获取目前头节点的元素
        E item = f.item;
        // 将目前头结点元素置空
        f.item = null;
        // 设置头节点的下一节点引用为自己
        f.next = f; // help GC
        // 设置新的头结点
        first = n;
        // 如果新的头结点为空则队列为空,设置尾节点也为空
        if (n == null)
            last = null;
        else
        	// 否则设置新的头节点的前一节点为空
            n.prev = null;
        // 当前队列的数量减一
        --count;
        // 唤醒等待写入队列的线程
        notFull.signal();
        return item;
    }
  • takeLast 从队列尾部读取数据
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();
        }
    }
  • unlinkLast 从双向链表尾部删除一条数据
private E unlinkLast() {
        // 获取尾节点,如果为空则直接返回
        Node<E> l = last;
        if (l == null)
            return null;
        // 获取尾节点的前一节点,做新的尾节点
        Node<E> p = l.prev;
        // 获取当前尾节点的元素
        E item = l.item;
        // 将当前尾节点的元素置空
        l.item = null;
        l.prev = l; // help GC
        // 设置新的尾节点
        last = p;
        // 如果新的尾节点为空表示队列为空,则头节点也为空
        if (p == null)
            first = null;
        else
        	// 设置新的尾节点的下一节点为空
            p.next = null;
        // 队列总数减一
        --count;
        // 唤醒等待写入队列的线程
        notFull.signal();
        return item;
    }

迭代器方法

  • iterator
public Iterator<E> iterator() {
        return new Itr();
    }
private class Itr extends AbstractItr {
    Node<E> firstNode() { return first; }
    Node<E> nextNode(Node<E> n) { return n.next; }
}

AbstractItr 类实现

private abstract class AbstractItr implements Iterator<E> {
        // next下一次调用返回的节点
        Node<E> next;

        // next下一次调用返回的元素
        E nextItem;

        // 上一个调用next返回的节点
        private Node<E> lastRet;

		// 获取头节点抽象方法
        abstract Node<E> firstNode();
        // 获取下一节点抽象方法
        abstract Node<E> nextNode(Node<E> n);

        AbstractItr() {
            // 获取当前队列的读写锁
            final ReentrantLock lock = LinkedBlockingDeque.this.lock;
            lock.lock();
            try {
            	// 设置下一个next调用节点为头节点
                next = firstNode();
                // 设置下一个next调用返回元素
                nextItem = (next == null) ? null : next.item;
            } finally {
                lock.unlock();
            }
        }

        // 获取链表的下一个节点
        private Node<E> succ(Node<E> n) {
            // Chains of deleted nodes ending in null or self-links
            // are possible if multiple interior nodes are removed.
            for (;;) {
                Node<E> s = nextNode(n);
                if (s == null)
                    return null;
                else if (s.item != null)
                    return s;
                else if (s == n)
                    return firstNode();
                else
                    n = s;
            }
        }

        // 更新next 和 nextItem 
        void advance() {
            final ReentrantLock lock = LinkedBlockingDeque.this.lock;
            lock.lock();
            try {
                // assert next != null;
                next = succ(next);
                nextItem = (next == null) ? null : next.item;
            } finally {
                lock.unlock();
            }
        }

        public boolean hasNext() {
            return next != null;
        }

		// 返回节点元素
        public E next() {
            if (next == null)
                throw new NoSuchElementException();
            lastRet = next;
            E x = nextItem;
            advance();
            return x;
        }

		// 删除元素
        public void remove() {
            Node<E> n = lastRet;
            if (n == null)
                throw new IllegalStateException();
            lastRet = null;
            final ReentrantLock lock = LinkedBlockingDeque.this.lock;
            lock.lock();
            try {
                if (n.item != null)
                    unlink(n);
            } finally {
                lock.unlock();
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值