java并发编程 LinkedBlockingDeque详解


java 并发编程系列文章目录

1 LinkedBlockingDeque是什么

首先queue是一种数据结构,一个集合中,先进后出,有两种实现的方式,数组和链表。从尾部追加,从头部获取。Deque是两端都可以添加,且两端都可以获取,所以它的方法会有一系列的Last,Frist语义,添加或获取等操作会指明哪个方向的,这也是Deque接口的定义。
那如果你不指定语义 如add()方法,他会默认调用addLast
综上所述,LinkedBlockingDeque是一个线程安全的双端阻塞队列。

2 核心属性详解

相对于LinkedBlockingQueue 他只能使用一把锁,不能分成put 和 take两把锁。因为此时双端都可以put 和 take,所以只能使用一个锁,通过锁,对其链表实现线程安全的操作。

	//队列的头尾节点
    transient Node<E> first;
    transient Node<E> last;

    //队列中元素的数量
    private transient int count;

    //指定的队列的容量,默认Int最大值
    private final int capacity;

    //实现线程安全的使用的锁
    final ReentrantLock lock = new ReentrantLock();

    //获取元素的时候如果空了会使用它让其自己等待
    private final Condition notEmpty = lock.newCondition();

    //添加元素的时候如果满了(count == capacity)会使用它让其自己等待
    private final Condition notFull = lock.newCondition();

3 核心方法详解

下面会列举First系列的方法,因为last系列相对于first只是链表方向不一样,操作都是一致的。

3.1 addFirst(E e)

调用offerFirst 如果未成功 则抛出异常

    public void addFirst(E e) {
        if (!offerFirst(e))
            throw new IllegalStateException("Deque full");
    }

3.2 offerFirst(E e)

在链表的头部添加一个元素,使用ReentrantLock 保证线程安全

    public boolean offerFirst(E e) {
        if (e == null) throw new NullPointerException();
        Node<E> node = new Node<E>(e);
        //获取锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        	//把当前元素对应的节点放到头结点那里
            return linkFirst(node);
        } finally {
            lock.unlock();
        }
    }
    private boolean linkFirst(Node<E> node) {
    	//如果元素已经超出容量,返回添加失败
        if (count >= capacity)
            return false;
        //链表的操作,用的是双向链表,first变成自己,之前的first是自己的next
        Node<E> f = first;
        node.next = f;
        first = node;
        if (last == null)
            last = node;
        else
            f.prev = node;
        //元素统计数量加1
        ++count;
        //唤醒那些因为获取不到元素而阻塞的线程
        notEmpty.signal();
        return true;
    }

3.3 putFirst(E e)

相对于offer一个元素 如果元素数量已到达容量上线,会阻塞住等待元素被取走才放入
在juc下面 put add take等语义都是一致的

    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();
        }
    }

3.4 removeFirst()

从头结点移除一个元素,调用的是pollFirst,拿出元素返回,元素==null会抛出异常

    public E removeFirst() {
        E x = pollFirst();
        if (x == null) throw new NoSuchElementException();
        return x;
    }

3.5 pollFirst()

取出first元素并返回,会返回null

    public E pollFirst() {
    	//加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
        	// 取出first, 链表的操作和count的维护以及唤醒添加元素因为容量到达上线的等待的线程
            return unlinkFirst();
        } finally {
            lock.unlock();
        }
    }

3.6 takeFirst()

获取一个first元素,区别poll 在于会阻塞等待

    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();
        }
    }

3.7 其他

对于last系列方法,只是链表的操作方向不一样而已
其次默认的不带last 和 first系列的方法,即原始的add put等方法,可以等同LinkedBlockingQueue。
LinkedBlockingDeque内部是一个双向链表,支持了链表两端操作,所以方法不一一介绍,原理都是一样。

4 总结

LinkedBlockingDeque使用双端队列,通过ReentrantLock保证线程安全,实现了双端的线程安全的阻塞队列。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值