前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >周末下午写的LinkedBlockingDeque源码分析

周末下午写的LinkedBlockingDeque源码分析

作者头像
码农王同学
发布2020-12-25 14:14:43
2790
发布2020-12-25 14:14:43
举报
文章被收录于专栏:后端Coder后端Coder

一,LinkedBlockingDeque源码分析

分析了好多集合源码之后,我也在想我这么做的初衷是否改变过?答案是未曾改变,写自己喜欢的内容才可持续地走下去,不然,只会搞得自己疲惫不堪。当然了,能帮助到需要的人是再好不过的了。

二,方法分析

2.1,构造函数

代码语言:javascript
复制
public LinkedBlockingDeque() {
        this(Integer.MAX_VALUE);
    }
//容量赋值操作
 public LinkedBlockingDeque(int capacity) {
     //参数校验,若小于0,直接抛出不合法的参数异常
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
    }

2.2,add()方法

代码语言:javascript
复制
public boolean add(E e) {
        addLast(e);
        return true;
    }
//方法的复用操作
public void addLast(E e) {
        if (!offerLast(e))
            throw new IllegalStateException("Deque full");
    }
//继续复用已有的方法
 public boolean offerLast(E e) {
     //首先,这个阻塞队列是不允许添加元素为null的情况的
     //否则直接抛出NPE异常
        if (e == null) throw new NullPointerException();
     //构造一个数据为e的节点node
        Node<E> node = new Node<E>(e);
     //获取锁实例对象,由此可见这是一个线程安全的方法
        final ReentrantLock lock = this.lock;
     //进行加锁操作
        lock.lock();
        try {
     //复用已有的方法,等下继续分析了
            return linkLast(node);
        } finally {
     //进行解锁操作,一定要放在finally语句块里进行
            lock.unlock();
        }
    }
// linkLast之所以单独抽取为一个方法是为了达到高度解耦
private boolean linkLast(Node<E> node) {
    //如果队列里面的元素个数count已经大于队列的容量了
    //说明队列已经满了,不允许再装入元素了
        if (count >= capacity)
            return false;
    //将最后一个节点引用赋值给临时局部变量l
        Node<E> l = last;
    //由于node节点之间是根据地址指针指向连接在一起的,所以这里node.prev=l;
        node.prev = l;
    //最后将新节点node赋值给最后一个节点last
        last = node;
    //为啥要在这里判断first==null呢?思考一下?
    //有可能第一次添加时,队列为空,所以此时node就是第一个节点了
        if (first == null)
            first = node;
        else
    //由于队列里面已经存在元素了,所以将node直接挂载在最后一个节点的后面
            l.next = node;
    //队列的元素个数增加一
        ++count;
    //进行线程间的通信机制,如何线程通信,在之前的文章已提到,可以去看下
        notEmpty.signal();
        return true;
    }

进行线程间的通信机制,如何线程通信,在之前的文章已提到,可以去看下这篇文章的最后。

2.3,offer()方法

代码语言:javascript
复制
public boolean offer(E e) {
        return offerLast(e);
    }
//复用已有的方法
public boolean offerLast(E e) {
    //这个方法已经在add()方法分析过了,在此不再分析了哈
        if (e == null) throw new NullPointerException();
        Node<E> node = new Node<E>(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return linkLast(node);
        } finally {
            lock.unlock();
        }
    }

offer()方法和add()方法都是在队列的尾部添加元素,其内部的实现都是一样的,这里你学习到方法的复用就可以了,这里就不过多分析了。我们接下来分析的都是如何向队列里放入元素。

2.4,putFirst()方法

代码语言:javascript
复制
public void putFirst(E e) throws InterruptedException {
    //这个方法就是在队列的队首位置添加元素,但是不允许添加元素为null的情况
        if (e == null) throw new NullPointerException();
    //构建一个元素为e的节点node
        Node<E> node = new Node<E>(e);
    //进行加减锁操作的步骤,主要是为了确保线程安全的
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //这是一个阻塞性的方法,看到这里的await就已经明白了吧
            while (!linkFirst(node))
                notFull.await();
        } finally {
            lock.unlock();
        }
    }
//linkFirst操作
private boolean linkFirst(Node<E> node) {
        if (count >= capacity)
            return false;
    //首先将队列的队首引用赋值给临时局部变量f
        Node<E> f = first;
    //由于新节点是需要放在队首位置的,所以node.next=f
        node.next = f;
    //新节点赋值给队首元素
        first = node;
    //如果last为null,说明队列里还没元素的,直接将node赋值给last
        if (last == null)
            last = node;
        else
            // f.prev=node操作是第二个元素的前一个指向就是node了
            f.prev = node;
    //队列里的元素个数加一
        ++count;
        notEmpty.signal();
        return true;
    }

2.5,offerFirst()方法

代码语言:javascript
复制
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();
        }
    }

2.6,addFirst()方法

代码语言:javascript
复制
public void addFirst(E e) {
     //如果添加失败,则抛出队列已满的异常给调用者
        if (!offerFirst(e))
            throw new IllegalStateException("Deque full");
    }

2.7,getFirst()方法

代码语言:javascript
复制
public E getFirst() {
    //只是获取了队列的队首元素,但是元素没有出队列
        E x = peekFirst();
        if (x == null) throw new NoSuchElementException();
        return x;
    }
//peekFirst()方法
 public E peekFirst() {
        final ReentrantLock lock = this.lock;
     //加锁
        lock.lock();
        try {
            //队列里面没有元素时,获取时,直接返回null,否则返回元素item
            return (first == null) ? null : first.item;
        } finally {
      //解锁操作
            lock.unlock();
        }
    }

2.8,getLast()方法

代码语言:javascript
复制
public E getLast() {
    //获取队列的最后一个元素
        E x = peekLast();
        if (x == null) throw new NoSuchElementException();
        return x;
    }
//线程安全的方法
 public E peekLast() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //获取队尾元素
            return (last == null) ? null : last.item;
        } finally {
            lock.unlock();
        }
    }

2.9,remove()方法

代码语言:javascript
复制
public E remove() {
        return removeFirst();
    }
//移除队首元素
public E removeFirst() {
        E x = pollFirst();
        if (x == null) throw new NoSuchElementException();
        return x;
    }
//线程安全的方法
public E pollFirst() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return unlinkFirst();
        } finally {
            lock.unlock();
        }
    }
//unlinkFirst()方法
private E unlinkFirst() {
       //获取队首元素的引用
        Node<E> f = first;
    //若f==null,则表示队列没有元素,直接返回null即可
        if (f == null)
            return null;
    //取队列的第二个元素赋值给临时局部变量n
        Node<E> n = f.next;
    //获取队首元素item
        E item = f.item;
    //将item置为null,等待gc触发
        f.item = null;
        f.next = f; // help GC
    //将队列的第二个元素赋值给队首元素first
        first = n;
    //n==null表示队列里没有元素了
        if (n == null)
            last = null;
        else
     //n此时为队首元素,队首元素的prev是为null
            n.prev = null;
    //队列元素个数减一操作
        --count;
    //线程通信
        notFull.signal();
        return item;
    }

2.10,poll()方法

代码语言:javascript
复制
public E poll() {
        return pollFirst();
    }
// pollFirst()方法也是一个线程安全的方法
public E pollFirst() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //这个方法已经分析过了,这个poll()方法表示队列的元素出队列
            return unlinkFirst();
        } finally {
            lock.unlock();
        }
    }

2.11,take()方法

take()方法是一个阻塞的方法,也是获取队首的元素,队列里面就没有这个元素了

代码语言:javascript
复制
public E take() throws InterruptedException {
        return 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();
        }
    }

2.12,element()方法

代码语言:javascript
复制
public E element() {
     //获取队首元素,但是不出队列
        return getFirst();
    }

2.13,peek()方法

代码语言:javascript
复制
public E peek() {
     //获取队首元素
        return peekFirst();
    }

2.14,remainingCapacity()方法

代码语言:javascript
复制
public int remainingCapacity() {
    //线程安全的方法
        final ReentrantLock lock = this.lock;
    //加锁
        lock.lock();
        try {
            //容量减去已有的队列元素个数,就是队列的剩余容量了
            return capacity - count;
        } finally {
            //解锁
            lock.unlock();
        }
    }

2.15,size()方法

代码语言:javascript
复制
public int size() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //返回成员变量count,即队列元素个数
            return count;
        } finally {
            lock.unlock();
        }
    }

2.16,contains()方法

代码语言:javascript
复制
public boolean contains(Object o) {
    //前置校验,若元素为null,则直接返回false
        if (o == null) return false;
    //获取锁实例对象
        final ReentrantLock lock = this.lock;
    //加锁操作
        lock.lock();
        try {
            //循环遍历队列的每一个元素
            for (Node<E> p = first; p != null; p = p.next)
                //若元素相等,则表示存在,直接返回true
                if (o.equals(p.item))
                    return true;
            return false;
        } finally {
            //解锁操作
            lock.unlock();
        }
    }

2.17,clear()方法

代码语言:javascript
复制
public void clear() {     //线程安全
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //循环遍历队列的每一个元素,将队列的每一个元素置为null
            //等待gc触发,回收不可达对象
            for (Node<E> f = first; f != null; ) {
                //元素置为null
                f.item = null;
                //队列的下一个元素赋值给临时局部变量n
                Node<E> n = f.next;
                //队列的队首元素的f.prev置为null
                f.prev = null;
                //队列的队首元素的next置为null
                f.next = null;
                //将队列的下一个元素变为队首元素,即f=n
                f = n;
            }
            //当队列的每一个元素都处理完了,即first=last=null
            first = last = null;
            //队列元素个数置为0
            count = 0;
            notFull.signalAll();
        } finally {
            //解锁操作
            lock.unlock();
        }
    }

关于push()方法和pop()方法这里都不分析了,都大同小异,理解其它方法就可以了。

2.18,removeFirstOccurrence()方法

代码语言:javascript
复制
public boolean removeFirstOccurrence(Object o) {
        if (o == null) return false;
    //线程安全
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //循环判断队列的每一个元素,找到元素o,然后进行删除
            for (Node<E> p = first; p != null; p = p.next) {
                if (o.equals(p.item)) {
                    unlink(p);
                    return true;
                }
            }
            return false;
        } finally {
            //解锁操作
            lock.unlock();
        }
    }
//unlink()方法
void unlink(Node<E> x) {
        //找到待删除元素的前一个元素p
        Node<E> p = x.prev;
    //找到待删除元素的下一个元素n
        Node<E> n = x.next;
    //如果p==null表示待删除的元素是队首元素
        if (p == null) {
            unlinkFirst();
    //如果n==null,表示待删除的元素是队尾元素
        } else if (n == null) {
            //继续看下unlinkLast()方法
            unlinkLast();
        } else {
      //这个时候,就是由可能删除的是中间元素
      //p.next=n和n.prev=p就是为了连接队列的每一个元素
            p.next = n;
            n.prev = p;
      //触发gc
            x.item = null;
      //队列元素个数减一
            --count;
            notFull.signal();
        }
    }
//unlinkLast()方法
 private E unlinkLast() {
        //将队列的队尾赋值给临时局部变量l
        Node<E> l = last;
        if (l == null)
            return null;
     //将最后一个元素置为l
        Node<E> p = l.prev;
        E item = l.item;
        l.item = null;
        l.prev = l; // help GC
     //队尾元素的前一个元素作为最后一个节点引用,即last=p
        last = p;
     //如果p==null说明队列没有元素了,即first==null
        if (p == null)
            first = null;
        else
            //队列里还有元素,但是队尾元素的next需要置为null,因为队尾元素的下一个指向next就是null
            p.next = null;
     //队列元素个数减一
        --count;
        notFull.signal();
        return item;
    }

三,总结一下

本篇提到最多的就是线程安全的关键字了,如何理解线程安全是后面文章可能会写的内容,差不多这篇文章分析完了之后,集合的源码分析就结束了,后面看下自己是否还有喜欢的文章可以分析了。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一,LinkedBlockingDeque源码分析
  • 二,方法分析
    • 2.1,构造函数
      • 2.2,add()方法
        • 2.3,offer()方法
          • 2.4,putFirst()方法
            • 2.5,offerFirst()方法
              • 2.6,addFirst()方法
                • 2.7,getFirst()方法
                  • 2.8,getLast()方法
                    • 2.9,remove()方法
                      • 2.10,poll()方法
                        • 2.11,take()方法
                          • 2.12,element()方法
                            • 2.13,peek()方法
                              • 2.14,remainingCapacity()方法
                                • 2.15,size()方法
                                  • 2.16,contains()方法
                                    • 2.17,clear()方法
                                      • 2.18,removeFirstOccurrence()方法
                                      • 三,总结一下
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档