• 中文
    • English
  • 注册
  • 查看作者
  • JAVA concurrency — ArrayBlockingQueue源码详解

    概述

    顾名思义,使用数组实现的阻塞队列。今天我们就来详细讲述下他的代码实现

    阻塞队列

    什么是阻塞队列?

    阻塞队列是一种特殊的队列,使用场景为并发环境下。在某种情况下(当线程无法获取锁的时候)线程会被挂起并且在队列中等待,如果条件具备(锁被释放)那么就会唤醒挂起的线程。

    通俗点来讲的话,阻塞队列类似于理发店的等待区,当没有理发师空闲的时候,客人会在等待区等待,一旦有了空闲,就会有人自动递补。

    类的继承关系

    JAVA concurrency — ArrayBlockingQueue源码详解

    继承了抽象队列,并且实现了阻塞队列,因此它具备队列的所有基本特性。

    基本实现原理

    的实现是基于 以及 内部实现的锁机制以及 机制。 内部声明了两个 变量,一个叫 ,一个叫 ,当有数据加入队列时尝试唤醒 ,当有数据移除队列时则唤醒 ,从而实现一个类似于生产者消费者模型的机制。

    源码分析

    类成员变量

    构造函数

    这里有一点疑问,这里明明是构造函数,是类初始化的地方,照理来说不会产生竞争,为什么要进行加锁操作呢?此处原本有一句原版的注释 锁是为了可见性而不是互斥。这句话怎么理解呢?我们仔细观察代码,发现当我们把集合中的数据全部插入队列中之后,我们会修改相应的 以及 的数值,但是如果我们没有加锁,那么在集合插入完成前 以及 没有完成初始化操作的时候如果有其他线程进行了插入等操作的话,会造成数据同步问题从而使得数据不准确,因此这里的锁是必要的。

    队列操作

    基础队列操作enqueue和dequeue

    这两个方法是队列操作的基本方法,基本上就是常规的数组数据插入移除,只是有一点很让人困惑 这段代码实现将类成员对象在本地创建了一个引用,然后在本地使用引用进行操作,为什么要多此一举呢?除此之外,代码中大量用到了这种手法,例如: 这又是为了什么呢?对此笔者猜测可能是和优化相关,因为jdk7中的实现与之不同,是使用的类变量直接操作。在进行了资料查阅后,笔者找到了一个相对靠谱的解释:

    当然也有一些大神有一些其他的解释:

    希望大家可以自己认真思考下,然后尝试下,得到自己的结论。

    阻塞队列的插入操作

    阻塞队列插入操作大致就以上几种,这几种的区别在代码中也体现得比较清楚了:

    1. 返回的是布尔值,插入成功返回 否则(队列已满)返回

    2. 没有返回值,假如队列是满的,他会一直阻塞直到队列为空的时候执行插入操作

    3. 实际上调用的就是 ,只是他在加入失败后会抛出异常

    阻塞队列的移除操作

    1. 执行成功会返回队列元素,如果队列为空则直接返回null

    2. 执行成功会返回队列元素,但是如果队列为空他不会返回而是等待有数据插入,然后取出

    3. 则是直接获取队列元素,并且执行后不会将元素从队列中删除

    迭代器实现

    由于迭代器和内部队列共享数据,再加上阻塞队列的特性,导致为了实现迭代器功能,需要新增一些很复杂的代码实现。

    内部声明了两个类来实现迭代器,一个是 继承 ,一个则是 。

    Itrs

    是用来管理迭代器的。由于阻塞队列内部可能会有多个迭代器在同时工作,在迭代器内部发生删除或者是一些不常见的操作时可能会产生一些问题,比如他们会丢失自己的数据之类的。所以 内部会维护一个变量用于记录循环的圈数,并且在删除操作 的时候会通知所有的迭代器。

    Itr

    是管理迭代器的, 则是迭代器的具体实现

    总结

    的实现可以说是比较的简单清晰,主要是利用了 内部的 ,通过设置两个条件来巧妙地完成阻塞队列的实现,只要能够理解这两个条件的工作原理,源码的理解就没有太大的难度。 较难理解的反而是它内部的迭代器,由于阻塞队列的特性,他的迭代器可能会有丢失当前数据的风险,因此,作者创作的时候加入了许多复杂的方法来保证可靠性,但是在这里由于篇幅限制,以及迭代器在阻塞队列中的地位和重要性并不高,所以简单讲述,如果有兴趣可以自己找一份源码阅读。

  • 0
  • 0
  • 0
  • 31
  • 请登录之后再进行评论

    登录
  • 任务
  • 实时动态
  • 发布
  • 单栏布局 侧栏位置: