• LinkedBlockingQueue源码解析(2)


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

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


    3.3、public void put(E e) throws InterruptedException

    原理:

    • 在队尾插入一个元素,如果队列满了,一直阻塞,直到队列不满了或者线程被中断

    使用方法:

            try {
                abq.put("hello1");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

    源代码:

        /**
         * 在队尾插一个元素
         * 如果队列满了,一直阻塞,直到队列不满了或者线程被中断
         */
        public void put(E e) throws InterruptedException {
            if (e == null) throw new NullPointerException();
            int c = -1;
            final ReentrantLock putLock = this.putLock;//入队锁
            final AtomicInteger count = this.count;//当前队列中的元素个数
            putLock.lockInterruptibly();//加锁
            try {
                while (count.get() == capacity) {//如果队列满了 
                    /*
                     * 加入notFull等待队列,直到队列元素不满了,
                     * 被其他线程使用notFull.signal()唤醒
                     */
                    notFull.await();
                }
                enqueue(e);//入队
                c = count.getAndIncrement();//入队数量+1
                if (c + 1 < capacity)
                    notFull.signal();
            } finally {
                putLock.unlock();
            }
            if (c == 0)
                signalNotEmpty();
        }

     

    4、出队

    4.1、public E poll()

    原理:

    • 如果没有元素,直接返回null;如果有元素,出队

    使用方法:

    abq.poll();

    源代码:

        /**
         * 出队: 
         * 1、如果没有元素,直接返回null 
         * 2、如果有元素,出队
         */
        public E poll() {
            final AtomicInteger count = this.count;// 获取元素数量
            if (count.get() == 0)// 没有元素
                return null;
            E x = null;
            int c = -1;
            final ReentrantLock takeLock = this.takeLock;
            takeLock.lock();// 获取出队锁
            try {
                if (count.get() > 0) {// 有元素
                    x = dequeue();// 出队
                    // 元素个数-1(注意:该方法是一个无限循环,直到减1成功为止,且返回旧值)
                    c = count.getAndDecrement();
                    if (c > 1)// 还有元素(如果旧值c==1的话,那么通过上边的操作之后,队列就空了)
                        notEmpty.signal();// 唤醒等待在notEmpty队列中的其中一条线程
                }
            } finally {
                takeLock.unlock();// 释放出队锁
            }
            if (c == capacity)// c == capacity是怎么发生的?如果队列是一个满队列,注意:上边的c返回的是旧值
                signalNotFull();
            return x;
        }

        /**
         * 从队列头部移除一个节点
         */
        private E dequeue() {
            Node<E> h = head;//获取头节点:x==null
            Node<E> first = h.next;//将头节点的下一个节点赋值给first
            h.next = h; // 将当前将要出队的节点置null(为了使其做head节点做准备)
            head = first;//将当前将要出队的节点作为了头节点
            E x = first.item;//获取出队节点的值
            first.item = null;//将出队节点的值置空
            return x;
        }

        private void signalNotFull() {
            final ReentrantLock putLock = this.putLock;
            putLock.lock();
            try {
                notFull.signal();
            } finally {
                putLock.unlock();
            }
        }

    注意:出队逻辑如果不懂,查看最后总结部分的图

     

    4.2、public E poll(long timeout, TimeUnit unit) throws InterruptedException

    原理:

    • 从队头删除一个元素,如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:

      • 被唤醒

      • 等待时间超时

      • 当前线程被中断

    使用方法:

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

    源代码:

        /**
         * 从队列头部删除一个元素,
         * 如果队列不空,出队;
         * 如果队列已空,判断时间是否超时,如果已经超时,返回null
         * 如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:
         * 1、被唤醒
         * 2、等待时间超时
         * 3、当前线程被中断
         */
        public E poll(long timeout, TimeUnit unit) throws InterruptedException {
            E x = null;
            int c = -1;
            long nanos = unit.toNanos(timeout);
            final AtomicInteger count = this.count;
            final ReentrantLock takeLock = this.takeLock;
            takeLock.lockInterruptibly();
            try {
                while (count.get() == 0) {//如果队列没有元素
                    if (nanos <= 0)//已经超时
                        return null;
                    /*
                     * 进行等待:
                     * 在这个过程中可能发生三件事:
                     * 1、被唤醒-->继续当前这个while循环
                     * 2、超时-->继续当前这个while循环
                     * 3、被中断-->抛出异常
                     */
                    nanos = notEmpty.awaitNanos(nanos);
                }
                x = dequeue();//出队
                c = count.getAndDecrement();
                if (c > 1)
                    notEmpty.signal();
            } finally {
                takeLock.unlock();
            }
            if (c == capacity)
                signalNotFull();
            return x;
        }

     


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

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


    相关文章:
    【推荐】 基于开源,强于开源,轻舟微服务解决方案深度解读
    【推荐】 云计算交互设计师的正确出装姿势

  • 相关阅读:
    arcgis api for javascipt 加载天地图、百度地图
    百度地图通过经纬度获取地址信息
    通过百度获取IP地址对应的经纬度
    黑马lavarel教程---13、分页
    legend3---6、legend3爬坑杂记
    黑马vue---1-7、vue杂记
    黑马在线教育项目---15-16、datatables插件
    尚学堂requireJs课程---3、私有和公有属性和方法
    尚学堂requireJs课程---2、模块
    尚学堂requireJs课程---1、作用域回顾
  • 原文地址:https://www.cnblogs.com/163yun/p/10137534.html
Copyright © 2020-2023  润新知