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

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

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

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

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

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

LinkedBlockingQueue源码解析(2)的更多相关文章

  1. LinkedBlockingQueue源码解析

    上一篇博客,我们介绍了ArrayBlockQueue,知道了它是基于数组实现的有界阻塞队列,既然有基于数组实现的,那么一定有基于链表实现的队列了,没错,当然有,这就是我们今天的主角:LinkedBlo ...

  2. LinkedBlockingQueue源码解析(3)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.3.public E take() throws InterruptedException 原理: 将队 ...

  3. 第九章 LinkedBlockingQueue源码解析

    1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...

  4. Java并发包源码学习系列:阻塞队列实现之LinkedBlockingQueue源码解析

    目录 LinkedBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e ...

  5. LinkedBlockingQueue源码解析(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2 ...

  6. Java并发包源码学习系列:阻塞队列实现之PriorityBlockingQueue源码解析

    目录 PriorityBlockingQueue概述 类图结构及重要字段 什么是二叉堆 堆的基本操作 向上调整void up(int u) 向下调整void down(int u) 构造器 扩容方法t ...

  7. Java并发包源码学习系列:阻塞队列实现之DelayQueue源码解析

    目录 DelayQueue概述 类图及重要字段 Delayed接口 Delayed元素案例 构造器 put take first = null 有什么用 总结 参考阅读 系列传送门: Java并发包源 ...

  8. Java并发包源码学习系列:阻塞队列实现之SynchronousQueue源码解析

    目录 SynchronousQueue概述 使用案例 类图结构 put与take方法 void put(E e) E take() Transfer 公平模式TransferQueue QNode t ...

  9. Java并发包源码学习系列:阻塞队列实现之LinkedTransferQueue源码解析

    目录 LinkedTransferQueue概述 TransferQueue 类图结构及重要字段 Node节点 前置:xfer方法的定义 队列操作三大类 插入元素put.add.offer 获取元素t ...

随机推荐

  1. https://www.w3.org/

    W3C   W3C By Region All Australia Österreich (Austria) België (Belgium) Botswana Brasil (Brazil) 中国 ...

  2. HDU_5688

    /* 度熊所居住的 D 国,是一个完全尊重人权的国度.以至于这个国家的所有人命名自己的名字都非常奇怪.一个人的名字由若干个字符组成,同样的,这些字符的全排列的结果中的每一个字符串,也都是这个人的名字. ...

  3. POJ1180 Batch Scheduling -斜率优化DP

    题解 将费用提前计算可以得到状态转移方程: $F_i = \min(F_j + sumT_i * (sumC_i - sumC_j) + S \times (sumC_N - sumC_j)$ 把方程 ...

  4. azman使用笔记

    azman.msc 打开 容易冲突,要用lock 效率问题,可用sql azman

  5. RESTful Web API 实践

    REST 概念来源 网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机.平板.桌面电脑.其他专用设备...). 因此,必须有一种统一的机制,方便不同的前端设备与后端进行通 ...

  6. hadoop学习笔记(六):HBase体系结构和数据模型

    1. HBase体系结构 一个完整分布式的HBase的组成示意图如下,后面我们再详细谈其工作原理. 1)Client 包含访问HBase的接口并维护cache来加快对HBase的访问. 2)Zooke ...

  7. 学习前端的菜鸡对JS 的classList理解

    classList 在早期的时候要添加,删除类 需要用className去获取,然后通过正则表达式去判断这个类是否存在. 代码上去会有点麻烦,现在有了classList 就方便了很多. ——————— ...

  8. 使用原生Java Web来实现大文件的上传

    版权所有 2009-2018荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/up6.2/in ...

  9. 15) maven dependency scope

    Dependency Scope Dependency scope is used to limit the transitivity of a dependency, and also to aff ...

  10. 如何更新world文档的目录

    在想要设置目录的文档页,右键 -> 更新域, 或者在想要设置目录的文档页,按下 F9 即可 拓展: 在目录文档页  ,按Ctrl 并且单击鼠标可以跟踪目标连接 如果内容对您有所帮助,请打赏--- ...