• LinkedBlockingQueue源码解析(1)


    此文已由作者赵计刚授权网易云社区发布。

    欢迎访问网易云社区,了解更多网易技术产品运营经验。


    1、对于LinkedBlockingQueue需要掌握以下几点

    • 创建

    • 入队(添加元素)

    • 出队(删除元素)

    2、创建

    Node节点内部类与LinkedBlockingQueue的一些属性

        static class Node<E> {
            E item;//节点封装的数据
            /**
             * One of:
             * - the real successor Node
             * - this Node, meaning the successor is head.next
             * - null, meaning there is no successor (this is the last node)
             */
    
            Node<E> next;//下一个节点
            Node(E x) { item = x; }
        }
    
        /** 指定链表容量  */
        private final int capacity;
    
        /** 当前的元素个数 */
        private final AtomicInteger count = new AtomicInteger(0);
    
        /** 链表头节点 */
        private transient Node<E> head;
    
        /** 链表尾节点 */
        private transient Node<E> last;
    
        /** 出队锁 */
        private final ReentrantLock takeLock = new ReentrantLock();
    
        /** 出队等待条件 */
        private final Condition notEmpty = takeLock.newCondition();
    
        /** 入队锁 */
        private final ReentrantLock putLock = new ReentrantLock();
    
        /** 入队等待条件 */
        private final Condition notFull = putLock.newCondition();

    2.1、public LinkedBlockingQueue(int capacity)

    使用方法:

    Queue<String> abq = new LinkedBlockingQueue<String>(1000);

    源代码:

        /**
         * 创建一个 LinkedBlockingQueue,容量为指定容量
         */
        public LinkedBlockingQueue(int capacity) {
            if (capacity <= 0) throw new IllegalArgumentException();
            this.capacity = capacity;
            last = head = new Node<E>(null);//初始化头节点和尾节点,均为封装了null数据的节点
        }

    注意点:

    • LinkedBlockingQueue的组成一个链表+两把锁+两个条件

     

    2.2、public LinkedBlockingQueue()

    使用方法:

    Queue<String> abq = new LinkedBlockingQueue<String>();

    源代码:

        /**
         * 创建一个LinkedBlockingQueue,容量为整数最大值
         */
        public LinkedBlockingQueue() {
            this(Integer.MAX_VALUE);
        }

    注意点:默认容量为整数最大值,可以看做没有容量限制

     

    3、入队:

    3.1、public boolean offer(E e)

    原理:

    • 在队尾插入一个元素, 如果队列没满,立即返回true; 如果队列满了,立即返回false

    使用方法:

    • abq.offer("hello1");

    源代码:

        /**
         * 在队尾插入一个元素, 容量没满,可以立即插入,返回true; 队列满了,直接返回false
         * 注:如果使用了限制了容量的队列,这个方法比add()好,因为add()插入失败就会抛出异常
         */
        public boolean offer(E e) {
            if (e == null)
                throw new NullPointerException();
            final AtomicInteger count = this.count;// 获取队列中的元素个数
            if (count.get() == capacity)// 队列满了
                return false;
            int c = -1;
            final ReentrantLock putLock = this.putLock;
            putLock.lock();// 获取入队锁
            try {
                if (count.get() < capacity) {// 容量没满
                    enqueue(e);// 入队
                    c = count.getAndIncrement();// 容量+1,返回旧值(注意)
                    if (c + 1 < capacity)// 如果添加元素后的容量,还小于指定容量(说明在插入当前元素后,至少还可以再插一个元素)
                        notFull.signal();// 唤醒等待notFull条件的其中一个线程
                }
            } finally {
                putLock.unlock();// 释放入队锁
            }
            if (c == 0)// 如果c==0,这是什么情况?一开始如果是个空队列,就会是这样的值,要注意的是,上边的c返回的是旧值
                signalNotEmpty();
            return c >= 0;
        }

        /**
         * 创建一个节点,并加入链表尾部
         * @param x
         */
        private void enqueue(E x) {
            /*
             * 封装新节点,并赋给当前的最后一个节点的下一个节点,然后在将这个节点设为最后一个节点
             */
            last = last.next = new Node<E>(x);
        }

        private void signalNotEmpty() {
            final ReentrantLock takeLock = this.takeLock;
            takeLock.lock();//获取出队锁
            try {
                notEmpty.signal();//唤醒等待notEmpty条件的线程中的一个
            } finally {
                takeLock.unlock();//释放出队锁
            }
        }

    如果,入队逻辑不懂,查看最后总结部分入队逻辑的图,代码非常简单,流程看注释即可。

     

    3.2、public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException

    原理:

    • 在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:

      • 被唤醒

      • 等待时间超时

      • 当前线程被中断

    使用方法:

            try {
                abq.offer("hello2",1000,TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

    源代码:

        /**
         * 在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况: 
         * 1、被唤醒 
         * 2、等待时间超时 
         * 3、当前线程被中断
         */
        public boolean offer(E e, long timeout, TimeUnit unit)
                throws InterruptedException {
    
            if (e == null)
                throw new NullPointerException();
            long nanos = unit.toNanos(timeout);// 转换为纳秒
            int c = -1;
            final ReentrantLock putLock = this.putLock;// 入队锁
            final AtomicInteger count = this.count;// 总数量
            putLock.lockInterruptibly();
            try {
                while (count.get() == capacity) {// 容量已满
                    if (nanos <= 0)// 已经超时
                        return false;
                    /*
                     * 进行等待: 在这个过程中可能发生三件事: 
                     * 1、被唤醒-->继续当前这个while循环
                     * 2、超时-->继续当前这个while循环 
                     * 3、被中断-->抛出中断异常InterruptedException
                     */
                    nanos = notFull.awaitNanos(nanos);
                }
                enqueue(e);// 入队
                c = count.getAndIncrement();// 入队元素数量+1
                if (c + 1 < capacity)
                    notFull.signal();
            } finally {
                putLock.unlock();
            }
            if (c == 0)
                signalNotEmpty();
            return true;
        }

    注意:

    • awaitNanos(nanos)是AQS中的一个方法,这里就不详细说了,有兴趣的自己去查看AQS的源代码。


    免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

    更多网易技术、产品、运营经验分享请点击


    相关文章:
    【推荐】 面对内容监管,短视频平台如何“翩翩起舞”,这有5条中肯建议
    【推荐】 小企业需要数据分析吗?

  • 相关阅读:
    2.7OpenIdConnectHandler 【RemoteAuthenticationHandler、IAuthenticationSignOutHandler】
    2.6OAuthHandler【RemoteAuthenticationHandler】
    2.0AuthenticationHandler【IAuthenticationHandler 】
    2.5RemoteAuthenticationHandler【AuthenticationHandler、IAuthenticationRequestHandler】
    2.4JwtBearerHandler 【AuthenticationHandler】
    在Centos7上安装Nominatim
    .net core使用 ELK
    linux 韩顺平课程笔记 3.11包管理工具(RPM和YUM)
    linux 韩顺平课程笔记 3.10进程管理
    linux 韩顺平课程笔记 3.5实用指令
  • 原文地址:https://www.cnblogs.com/zyfd/p/10137337.html
Copyright © 2020-2023  润新知