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

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

4.3、public E take() throws InterruptedException

原理:

  • 将队头元素出队,如果队列空了,一直阻塞,直到队列不为空或者线程被中断

使用方法:

        try {
            abq.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

源代码:

    /**
     * 出队:
     * 如果队列空了,一直阻塞,直到队列不为空或者线程被中断
     */
    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(即被其他线程唤醒)
                 * (唤醒其实就是,有线程将一个元素入队了,然后调用notEmpty.signal()唤醒其他等待这个条件的线程,同时队列也不空了)
                 */
                notEmpty.await();
            }
            x = dequeue();//出队
            c = count.getAndDecrement();//元素数量-1
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

总结:

1、具体入队与出队的原理图

图中每一个节点前半部分表示封装的数据x,后边的表示指向的下一个引用。

1.1、初始化

初始化之后,初始化一个数据为null,且head和last节点都是这个节点。

1.2、入队两个元素过后

这个可以根据入队方法enqueue(E x)来看,源代码再贴一遍:

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

其实这我们就可以发现其实真正意义上出队的头节点是Head节点的下一个节点。(这也就是Node这个内部类中对next的注释,我没有翻译)

1.3、出队一个元素后

表面上看,只是将头节点的next指针指向了要删除的x1.next,事实上这样我觉的就完全可以,但是jdk实际上是将原来的head节点删除了,而上边看到的这个head节点,正是刚刚出队的x1节点,只是其值被置空了。

这一块对应着源代码来看:dequeue()

    /**
     * 从队列头部移除一个节点
     */
    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;
    }

2、三种入队对比:

  • offer(E e):如果队列没满,立即返回true; 如果队列满了,立即返回false-->不阻塞

  • put(E e):如果队列满了,一直阻塞,直到队列不满了或者线程被中断-->阻塞

  • offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:-->阻塞

    • 被唤醒

    • 等待时间超时

    • 当前线程被中断

3、三种出队对比:

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

  • take():如果队列空了,一直阻塞,直到队列不为空或者线程被中断-->阻塞

  • poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:

    • 被唤醒

    • 等待时间超时

    • 当前线程被中断

4、ArrayBlockingQueue与LinkedBlockingQueue对比

  • ArrayBlockingQueue:

    • 一个对象数组+一把锁+两个条件

    • 入队与出队都用同一把锁

    • 在只有入队高并发或出队高并发的情况下,因为操作数组,且不需要扩容,性能很高

    • 采用了数组,必须指定大小,即容量有限

  • LinkedBlockingQueue:

    • 一个单向链表+两把锁+两个条件

    • 两把锁,一把用于入队,一把用于出队,有效的避免了入队与出队时使用一把锁带来的竞争。

    • 在入队与出队都高并发的情况下,性能比ArrayBlockingQueue高很多

    • 采用了链表,最大容量为整数最大值,可看做容量无限

 两个疑问:

  • 入队时:c==0是怎样出现的?

  • 出队时:c==capcity是怎样出现的?

这两个疑问,都是基于对于AtomicInteger的不熟,不明白LinkedBlockingQueue引用的这两个方法(getAndIncrement和getAndDecrement)先返回旧值还是新值,关于AtomicInteger的源码介绍,请查看《第十一章 AtomicInteger源码解析》,具体链接如下:

http://www.cnblogs.com/java-zhao/p/5140158.html

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

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

相关文章:
【推荐】 wireshark抓包分析——TCP/IP协议
【推荐】 漫画解读“跨视图粒度计算”,了解有数分析利器
【推荐】 Docker容器的原理与实践(上)

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

  1. LinkedBlockingQueue源码解析

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

  2. 第九章 LinkedBlockingQueue源码解析

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

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

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

  4. LinkedBlockingQueue源码解析(1)

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

  5. LinkedBlockingQueue源码解析(2)

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

  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. [leetcode]523. Continuous Subarray Sum连续子数组和(为K的倍数)

    Given a list of non-negative numbers and a target integer k, write a function to check if the array ...

  2. Java动态代理机制详解(类加载,JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  3. Hadoop 系列(一)基本概念

    Hadoop 系列(一)基本概念 一.Hadoop 简介 Hadoop 是一个由 Apache 基金会所开发的分布式系统基础架构,它可以使用户在不了解分布式底层细节的情況下开发分布式程序,充分利用集群 ...

  4. 《Just for Fun》---读后感

    <Just for Fun>本书是Linux之父林纳斯自传,书名的意思是:只是为了好玩.主要是讲了林纳斯的人生经历,以及Linux的诞生过程.Linux从一个终端仿真器到一个世界瞩目的操作 ...

  5. 《Delphi XE6 android 编程入门教程》推荐

    近5.6年已经没有看见关于delphi的新技术的书出来了(看来在国内delphi的使用量确实很低了), 高勇同学最近出了一本<Delphi XE6 android 编程入门教程>,上周刚拿 ...

  6. ThinkPHP5 union分页

    直接贴代码,记录一下,备用 $a = Db::name(表名)->field(字段)->where(条件)->buildSql(); $b = Db::name(表名)->fi ...

  7. redis学习-事务命令

    multi:开启事务 exec:提交事务 discard:取消事务 1.开启事务之后,每次执行命令之后,都要先进入事务队列中,只有在执行 exec之后才开始执行 2.开启事务之后,每次执行命令之后,都 ...

  8. S3 exercise -- 文件操作&函数

    编码 请说明python2 与python3中的默认编码是什么? # 答案 py2默认ASCII码,py3默认的utf8 为什么会出现中文乱码?你能列举出现乱码的情况有哪几种? # 答案 #codin ...

  9. 配置 struts2 时掉进 web.xml 的坑

    这个声明不好用 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN&quo ...

  10. QGIS+GH + MapServer

    拒绝描图,如何利用GH+QGIS完爆场地底图?http://www.sohu.com/a/251004986_657084 拒绝描图--爬取OSM数据绘制底图 所用软件 RHINO+GH\QGIS\G ...