2019独角兽企业重金招聘Python工程师标准>>>

前言

本文的主要详细分析ArrayBlockingQueue的实现原理,由于该并发集合其底层是使用了java.util.ReentrantLock和java.util.Condition来完成并发控制的,我们可以通过JDK的源代码更好的学习这些并发控制类的使用,同时该类也是所有并发集合中最简单的一个,分析该类的源码也是为之后分析其他并发集合做好基础。

1.Queue接口和BlockingQueue接口回顾

1.1 Queue接口回顾

在Queue接口中,除了继承Collection接口中定义的方法外,它还分别额外地定义插入、删除、查询这3个操作,其中每一个操作都以两种不同的形式存在,每一种形式都对应着一个方法。

方法说明:

操作 抛出异常 返回特殊值
Insert add(e) offer(e)
Remove remove() poll()
Examine element() peek()
  1. add方法在将一个元素插入到队列的尾部时,如果出现队列已经满了,那么就会抛出IllegalStateException,而使用offer方法时,如果队列满了,则添加失败,返回false,但并不会引发异常。
  2. remove方法是获取队列的头部元素并且删除,如果当队列为空时,那么就会抛出NoSuchElementException。而poll在队列为空时,则返回一个null。
  3. element方法是从队列中获取到队列的第一个元素,但不会删除,但是如果队列为空时,那么它就会抛出NoSuchElementException。peek方法与之类似,只是不会抛出异常,而是返回false。

后面我们在分析ArrayBlockingQueue的方法时,主要也是围绕着这几个方法来进行分析。

1.2 BlockingQueue接口回顾

BlockingQueue是JDK1.5出现的接口,它在原来的Queue接口基础上提供了更多的额外功能:当获取队列中的头部元素时,如果队列为空,那么它将会使执行线程处于等待状态;当添加一个元素到队列的尾部时,如果队列已经满了,那么它同样会使执行的线程处于等待状态。

前面我们在说Queue接口时提到过,它针对于相同的操作提供了2种不同的形式,而BlockingQueue更夸张,针对于相同的操作提供了4种不同的形式。

该四种形式分别为:

  • 抛出异常
  • 返回一个特殊值(可能是null或者是false,取决于具体的操作)
  • 阻塞当前执行直到其可以继续
  • 当线程被挂起后,等待最大的时间,如果一旦超时,即使该操作依旧无法继续执行,线程也不会再继续等待下去。

对应的方法说明:

操作 抛出异常 返回特殊值 阻塞 超时
Insert add(e) offer(e) put(e) offer(e, time, unit)
Remove remove() poll() take() poll(time, unit)
Examine element() peek()

BlockingQueue虽然比起Queue在操作上提供了更多的支持,但是它在使用的使用也应该如下的几点:

  1. BlockingQueue中是不允许添加null的,该接受在声明的时候就要求所有的实现类在接收到一个null的时候,都应该抛出NullPointerException。
  1. BlockingQueue是线程安全的,因此它的所有和队列相关的方法都具有原子性。但是对于那么从Collection接口中继承而来的批量操作方法,比如addAll(Collection e)等方法,BlockingQueue的实现通常没有保证其具有原子性,因此我们在使用的BlockingQueue,应该尽可能地不去使用这些方法。
  2. BlockingQueue主要应用于生产者与消费者的模型中,其元素的添加和获取都是极具规律性的。但是对于remove(Object o)这样的方法,虽然BlockingQueue可以保证元素正确的删除,但是这样的操作会非常响应性能,因此我们在没有特殊的情况下,也应该避免使用这类方法。

2. ArrayBlockingQueue深入分析

有了上面的铺垫,下面我们就可以真正开始分析ArrayBlockingQueue了。在分析之前,首先让我们看看API对其的描述。
注意:这里使用的JDK版本为1.7,不同的JDK版本在实现上存在不同

ArrayBlockingQueueAPI说明.png

首先让我们看下ArrayBlockingQueue的核心组成:

 /** 底层维护队列元素的数组 */final Object[] items;/**  当读取元素时数组的下标(这里称为读下标) */int takeIndex;/** 添加元素时数组的下标 (这里称为写小标)*/int putIndex;/** 队列中的元素个数 */int count;/**用于并发控制的工具类**/final ReentrantLock lock;/** 控制take操作时是否让线程等待 */private final Condition notEmpty;/** 控制put操作时是否让线程等待 */private final Condition notFull;

take方法分析(369-379行):

    public E take() throws InterruptedException {final ReentrantLock lock = this.lock;/*尝试获取锁,如果此时锁被其他线程锁占用,那么当前线程就处于Waiting的状态。           注意:当方法是支持线程中断响应的如果其他线程此时中断当前线程,那么当前线程就会抛出InterruptedException */lock.lockInterruptibly();try {/*如果此时队列中的元素个数为0,那么就让当前线程wait,并且释放锁。注意:这里使用了while进行重复检查,是为了防止当前线程可能由于  其他未知的原因被唤醒。(通常这种情况被称为"spurious wakeup")*/    while (count == 0)notEmpty.await();//如果队列不为空,则从队列的头部取元素return extract();} finally {//完成锁的释放lock.unlock();}}

extract方法分析(163-171):

    /*根据takeIndex来获取当前的元素,然后通知其他等待的线程。Call only when holding lock.(只有当前线程已经持有了锁之后,它才能调用该方法)*/private E extract() {final Object[] items = this.items;//根据takeIndex获取元素,因为元素是一个Object类型的数组,因此它通过cast方法将其转换成泛型。E x = this.<E>cast(items[takeIndex]);//将当前位置的元素设置为nullitems[takeIndex] = null;//并且将takeIndex++,注意:这里因为已经使用了锁,因此inc方法中没有使用到原子操作takeIndex = inc(takeIndex);//将队列中的总的元素减1--count;//唤醒其他等待的线程notFull.signal();return x;}

put方法分析(318-239)

public void put(E e) throws InterruptedException {//首先检查元素是否为空,否则抛出NullPointerExceptioncheckNotNull(e);final ReentrantLock lock = this.lock;//进行锁的抢占lock.lockInterruptibly();try {/*当队列的长度等于数组的长度,此时说明队列已经满了,这里同样使用了while来方式当前线程被"伪唤醒"。*/while (count == items.length)//则让当前线程处于等待状态notFull.await();//一旦获取到锁并且队列还未满时,则执行insert操作。insert(e);} finally {//完成锁的释放lock.unlock();}}//检查元素是否为空private static void checkNotNull(Object v) {if (v == null)throw new NullPointerException();}//该方法的逻辑非常简单private void insert(E x) {//将当前元素设置到putIndex位置   items[putIndex] = x;//让putIndex++putIndex = inc(putIndex);//将队列的大小加1++count;//唤醒其他正在处于等待状态的线程notEmpty.signal();}

注:ArrayBlockingQueue其实是一个循环队列
我们使用一个图来简单说明一下:

黄色表示数组中有元素

1-1.png

当再一次执行put的时候,其结果为:

1-2.png

此时放入的元素会从头开始置,我们通过其incr方法更加清晰的看出其底层的操作:

    /*** Circularly increment i.*/final int inc(int i) {//当takeIndex的值等于数组的长度时,就会重新置为0,这个一个循环递增的过程return (++i == items.length) ? 0 : i;}

至此,ArrayBlockingQueue的核心部分就分析完了,其余的队列操作基本上都是换汤不换药的,此处不再一一列举。

作者:码农一枚
链接:https://www.jianshu.com/p/9a652250e0d1
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载于:https://my.oschina.net/u/3658506/blog/1785528

【Java并发编程】—–“J.U.C”:ArrayBlockingQueue相关推荐

  1. 【java】java 并发编程 ArrayBlockingQueue

    文章目录 1.概述 1.1 介绍 1.2. 原理和数据结构 1.3. 函数列表 2.源码分析 2.1 创建 2.2 添加 2.3. 取出 2.4. 遍历 3. 示例 1.概述 首先看看文章:[java ...

  2. java并发编程:lock_编程的第五个十年:J代表Java

    java并发编程:lock 一段非常个人的编程历史中的第五章第一部分 在自己的一类 尽管Microsoft并不认同Oracle对Java的所有雄心壮志,但我们同意它对于软件开发人员来说是非常有价值的工 ...

  3. 干货:Java并发编程必懂知识点解析

    本文大纲 1.并发编程三要素 原子性 原子,即一个不可再被分割的颗粒.在Java中原子性指的是一个或多个操作要么全部执行成功要么全部执行失败. 有序性 程序执行的顺序按照代码的先后顺序执行.(处理器可 ...

  4. JAVA并发编程实践笔记

    2019独角兽企业重金招聘Python工程师标准>>> JAVA并发编程实践笔记 博客分类: java JAVA并发编程实践笔记 1, 保证线程安全的三种方法:     a, 不要跨 ...

  5. Java并发编程 基础知识学习总结

    Java并发编程一直是Java程序员必须懂但又是很难懂的技术内容,这部分的内容我也是反复学习了好几遍才能理解.本篇博客梳理一下最近从<Java 并发编程的艺术>和他人的博客学习Java并发 ...

  6. Java并发编程的艺术(推荐指数:☆☆☆☆☆☆)

    文章目录 Java并发编程的艺术(推荐指数:☆☆☆☆☆☆) 并发编程的挑战 Java并发机制的底层实现原理 Volatile的应用 实现原理 synchronized的实现原理与应用 对象头 锁详解 ...

  7. # Java 并发编程的艺术(一)

    Java 并发编程的艺术(一) 文章目录 Java 并发编程的艺术(一) Java中的线程池 线程池的实现原理 线程池的处理流程 ThreadPoolExecutor执行流程 线程池队列 线程池拒绝策 ...

  8. 【读书笔记】Java并发编程的艺术

    第一章 并发编程的挑战 上下文切换 上下文切换概述 切出:一个线程被剥夺处理器的使用权而暂定运行 切入:一个线程被选中占用处理器或者继续运行 上下文:在这种切入切出的过程中,操作系统需要保存和恢复相应 ...

  9. 《Java并发编程之美》

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAwEsYPZ-1661534116043)(img/\1625573175405.jpg)] [外链图片转存失败,源站 ...

  10. Java并发编程的艺术_Conc

    Java并发编程的艺术 1 并发编程的挑战 1.1 上下文切换 即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制.时间片是CPU分配给各个线程的时间,因为时间片 ...

最新文章

  1. 图形推理1000题pdf_笔试|图形推理题满分攻略
  2. mariadb数据库备份与恢复
  3. CentOS中设置ip地址等信息
  4. 最大熵对应的概率分布
  5. 汇编指令的学习1——ARM汇编的特点
  6. 怎样通过FineReader 的“文本”窗口检查文本
  7. 深度学习-Tensorflow1.x-CNN中的padding参数
  8. clojure 使用Lazy-seq创建斐波那契数列
  9. 同步图计算:GraphLite的安装和使用
  10. 电脑恶意软件删除方法
  11. win10重装系统自动修复失败,用U盘做了启动器也进不去PE界面的解决办法
  12. 2023齐齐哈尔大学计算机考研信息汇总
  13. 程序员副业赚钱之道,实现月收入增加20K
  14. 录屏怎么录?你知道多少录屏软件?
  15. 如何临时删除桌面右键菜单上的登录画面修改
  16. 小人数字时钟安卓版本APP
  17. CP和AP有是什么?有什么区别?
  18. 年中Flag拯救计划:寻找年中Flag挑战王!
  19. 起点编程工作室成立了
  20. 2022全国中职网络安全比赛正式赛题

热门文章

  1. idea 一键展开所有方法 一键收纳所有方法
  2. linux怎么开启httpd服务公钥,在Apache httpd服务器上部署SSL证书
  3. python爬虫数据存储文本_Python爬虫开发系列之五》数据存储为TXT、JSON格式
  4. 查看uboot变量地址_华为FIT AP通过Uboot切换FAT模式
  5. 学习ES6路线了解图
  6. pandas处理mysql 展现wpf_Pandas DataFrame使用多列聚合函数
  7. python约瑟夫环问题_约瑟夫环问题的Python实现
  8. IDEA导入Git中项目
  9. 10蓝牙_小米10手机专用?小米“真无线蓝牙耳机Air 2s”评测
  10. 软件测试需要哪些c语言基础知识,测试人员都需要了解哪些redis知识?