源码角度了解阻塞队列之LinkedBlockingQueue

举报
周杰伦本人 发表于 2022/09/29 23:43:46 2022/09/29
【摘要】 源码角度了解阻塞队列之LinkedBlockingQueueLinkedBlockingQueue是单向链表的阻塞队列,通过它的构造方法可以知道如果不指定容量的话,这个队列的长度Integer类型的最大值,它有两个锁和两个Condition,notEmpty = takeLock.newCondition();这是正在等待获取元素的等待队列,notFull = putLock.newCon...

源码角度了解阻塞队列之LinkedBlockingQueue

LinkedBlockingQueue是单向链表的阻塞队列,通过它的构造方法可以知道如果不指定容量的话,这个队列的长度Integer类型的最大值,它有两个锁和两个Condition,notEmpty = takeLock.newCondition();这是正在等待获取元素的等待队列,notFull = putLock.newCondition();这是正在等待放入元素的等待队列

count是AtomicInteger对象,保证原子性,因为put()方法和take()都会操作这个变量,需要保证原子性

put()方法

put()方法顾名思义就放入元素

public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
       
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }
  1. 加可中断锁
  2. 获取所有的元素个数
  3. 如果元素个数和总的容量相等的话,阻塞等待
  4. 没有达到最大容量数调用enqueue()方法将元素添加到阻塞队列中
  5. count值加1,这里getAndIncrement()返回的是旧值,然后再进行加一操作,如果得到值小于容量的话说明队列中还可以放入元素,唤醒notFull的线程
  6. 解锁
  7. 如果入队前为0,也就是入队后队列不为空了,就可以唤醒notEmpty的线程来取队列中的元素,signalNotEmpty()方法中需要获取takeLock锁才能调用notEmpty.signal()唤醒线程

take()方法

take()同样也是获取元素的功能

public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }
  1. 加可中断锁
  2. 获取所有的元素个数
  3. 如果为0的话,阻塞等待
  4. 不为0了调用dequeue()取出元素
  5. count值减一,如果得到值还大于1的话说明队列中还有元素,唤醒notEmpty的线程
  6. 解锁
  7. 如果出队前容量满了,出队后就还剩一个就唤醒NotFull的线程来添加元素,signalNotFull()方法中需要获取putLock锁才能调用notFull.signal()唤醒线程

总结

这篇文章我们从源码角度讲解了LinkedBlockingQueue的put()方法和take()方法,从源码中我们可以看出来,put()方法和take()各自有一把自己的锁,两个方法的逻辑差不多,一个是放入元素,一个是取出元素,采用了生产者消费者的思想,当队列空的时候,取元素的线程才会阻塞,当队列满的时候,放元素的线程才会阻塞

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。