多线程篇(阻塞队列- LinkedBlockingDeque)(持续更新迭代)

目录

一、LinkedBlockingDeque是什么

二、核心属性详解

三、核心方法详解

addFirst(E e)

offerFirst(E e)

putFirst(E e)

removeFirst()

pollFirst()

takeFirst()

其他

四、总结


一、LinkedBlockingDeque是什么

首先queue是一种数据结构,一个集合中,先进后出,有两种实现的方式,数组和链表。

从尾部追加,从头部获取。

Deque是两端都可以添加,且两端都可以获取,所以它的方法会有一系列的Last,Frist语义,添加或获取等操作

会指明哪个方向的,这也是Deque接口的定义。

那如果你不指定语义 如add()方法,他会默认调用addLast

综上所述,LinkedBlockingDeque是一个线程安全的双端阻塞队列。

二、核心属性详解

相对于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();

三、核心方法详解

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

addFirst(E e)

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

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

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

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

removeFirst()

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

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

pollFirst()

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


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

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

其他

对于last系列方法,只是链表的操作方向不一样而已

其次默认的不带last 和 first系列的方法,即原始的add put等方法,可以等同LinkedBlockingQueue。

LinkedBlockingDeque内部是一个双向链表,支持了链表两端操作,所以方法不一一介绍,原理都是一样。

四、总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wclass-zhengge

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值