Java8 ArrayBlockingQueue 源码阅读
一、什么是 ArrayBlockingQueue
ArrayBlockingQueue
是 GUC(java.util.concurrent) 包下的一个线程安全的阻塞队列,底层使用数组实现。
除了线程安全这个特性,ArrayBlockingQueue
还有其他的特点:
- 当队列已满时,会阻塞后面添加元素 [put(E e)] 的线程,直到调用了移除元素的方法队列不满的情况下会唤醒前面添加元素的线程
- 当队列已空时,会阻塞后面移除元素 [take()] 的线程,直到调用了添加元素的方法队列不为空的情况下会唤醒前面移除元素的线程
- 新添加的元素并不一定在数组的 0 下标位置,因为其内部维护了一个
putIndex
属性 - 数组大小确定,通过构造函数初始化阻塞队列大小,没有扩容机制,因为线程阻塞,不存在数组下标越界异常
- 元素都是紧凑的,比如阻塞队列中有两个元素,那这两个元素在数组中下标之差一定是 1
- 插入的元素不允许为 null,所有的队列都有这个特点
- 先进先出(FIFO (first-in-first-out))
二、相关结构介绍
2.1 内部属性
了解了 ArrayBlockingQueue
内部的属性,可以帮助我们更好的理解阻塞队列。
// 底层存储元素的数组final Object[] items;// 出队序号,如果有一个元素出队,那么后面的元素不会向前移动,// 而是将 takeIndex + 1 表示后面要出队的元素的角标int takeIndex;// 入队序号,表示后续插入的元素的角标,putIndex 不一定大于 takeIndexint putIndex;// 元素个数int count;// 内部锁final ReentrantLock lock;// notEmpty 条件private final Condition notEmpty;// notFull 条件private final Condition notFull;
2.2 构造函数
ArrayBlockingQueue
中共有 3 个构造函数,这里只看其中一个,拿出来简单地分析一下。
public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();// 初始化底层数组this.items = new Object[capacity];// 默认为非公平锁lock = new ReentrantLock(fair);notEmpty = lock.newCondition();notFull = lock.newCondition();}
2.3 相关方法特性
method name | usage |
---|---|
offer(E e) | 在队列尾部插入元素,如果队列已满,则返回 false |
put(E e) | 在队列尾部插入元素,如果队列已满,则线程阻塞等待空间可用 |
add(E e) | 底层调用了 offer(E e) 方法 |
poll() | 出队,如果队列中没有元素则返回 null |
take() | 出队,如果队列中没有元素,则线程阻塞,等待新元素插入 |
peek() | 出队,如果队列中没有元素则返回 null |
三、源码分析
上面对 ArrayBlockingQueue
中主要的方法作了介绍,并指出了对应特点,下面就简单的来分析一下 put
与 take
的方法。
3.1 put 方法
public void put(E e) throws InterruptedException {// 插入的元素不允许为 nullcheckNotNull(e);// 获取锁final ReentrantLock lock = this.lock;/*** lock:调用后一直阻塞到获得锁* tryLock:尝试是否能获得锁 如果不能获得立即返回* lockInterruptibly:调用后一直阻塞到获得锁 但是接受中断信号(比如:Thread、sleep)*/lock.lockInterruptibly();try {// 如果队列数组已满,则 notFull 阻塞,当有元素被移除后才唤醒 notFullwhile (count == items.length)notFull.await();// 元素入队enqueue(e);} finally {// 添加完元素后释放锁lock.unlock();}}
上面的方法中调用了 enqueue
方法,这个 enqueue
用于在队尾插入元素,下面是具体实现细节。
private void enqueue(E x) {// assert lock.getHoldCount() == 1;// assert items[putIndex] == null;final Object[] items = this.items;// 添加元素items[putIndex] = x; // 如果插入元素的位置是数组尾部则重置 putIndex 为 0if (++putIndex == items.length)putIndex = 0;count++;// 队列中一定有元素,因此唤醒 notEmptynotEmpty.signal();}
代码实现还是比较简单的,先加锁,如果队列没有满的情况下直接在 putIndex
的位置插入新元素,如果队列已满则阻塞当前获得锁的添加元素的线程,直到有元素从队列中被移除了,会唤醒 notFull
,添加元素的线程才会被唤醒继续执行。
3.2 take 方法
take
方法与 put
方法类似。
public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {// 如果队列中没有元素,则让 notEmpty 阻塞,添加元素后会唤醒 notEmptywhile (count == 0)notEmpty.await();return dequeue();} finally {lock.unlock();}}
出队 dequeue
源码。
private E dequeue() {// assert lock.getHoldCount() == 1;// assert items[takeIndex] != null;final Object[] items = this.items;@SuppressWarnings("unchecked")E x = (E) items[takeIndex];// 元素置 nullitems[takeIndex] = null;// 如果出队的是数组中的最后一个元素,则重置 takeIndex 为 0if (++takeIndex == items.length)takeIndex = 0;count--;if (itrs != null)itrs.elementDequeued();// 唤醒 notFullnotFull.signal();
出队与入队的原理都是类似的,同样是先加锁,如果队列中没有任何元素,则获得锁的出队的线程阻塞 notEmpty.await()
,直到有元素被添加到队列中,会唤醒 notEmpty
,移除元素的线程才会被唤醒继续执行,如果队列中有元素,则直接把 takeIndex
位置上的元素出队。
3.3 other
上面只简单的分析了 4 个方法,但是上面 4 个方法足以让我们了解 ArrayBlockingQueue
的实现原理。其中比较复杂的方法可能就是 remove
方法了,因为移除的元素可能在任意一个位置,为了使元素紧凑,会将后面的元素向前移动一个位置,然后重置 putIndex
,大概的流程就是这样,想看详细的源码可以点击后面的链接进行进一步的了解。
jdk1.8 源码阅读:https://github.com/zchen96/jdk1.8-source-code-read
Java8 ArrayBlockingQueue 源码阅读相关推荐
- Java8 Hashtable 源码阅读
一.Hashtable 概述 Hashtable 底层基于数组与链表实现,通过 synchronized 关键字保证在多线程的环境下仍然可以正常使用.虽然在多线程环境下有了更好的替代者 Concurr ...
- Java8 LinkedHashMap 源码阅读
如果你对 HashMap 的源码有了解的话,只需要一图就能知道 LinkedHashMap 的原理了,但是具体的实现细节还是需要去读一下源码. 一.LinkedHashMap 简介 1.1 继承结构 ...
- Java8 PriorityQueue 源码阅读
一.什么是 PriorityQueue 这篇文章带大家去了解一个 jdk 中不常用的数据结构 PriorityQueue(优先队列),虽然在项目里用的不多,但是它本身的设计实现还是很值得大家看一看的. ...
- Java8 ArrayBlockingQueue 源码解析
目录 1.定义 2.构造方法 3.add / offer / put 4.poll / take / peek 5.remove / clear /drainTo 6.iterator / Itr / ...
- 源码阅读(34):Java中线程安全的Queue、Deque结构——ArrayBlockingQueue(4)
(接上文<源码阅读(33):Java中线程安全的Queue.Deque结构--ArrayBlockingQueue(3)>) 2.3.3.3.forEachRemaining() 方法 f ...
- 源码阅读(32):Java中线程安全的Queue、Deque结构——ArrayBlockingQueue(2)
(接上文<源码阅读(31):Java中线程安全的Queue.Deque结构--ArrayBlockingQueue(1)>) 本篇内容我们专门分析ArrayBlockingQueue中迭代 ...
- Java8 ThreadLocal 源码分析
可参考文章: Java8 IdentityhashMap 源码分析 IdentityhashMap 与 ThreadLocalMap 一样都是采用线性探测法解决哈希冲突,有兴趣的可以先了解下 Iden ...
- 应用监控CAT之cat-client源码阅读(一)
CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...
- centos下将vim配置为强大的源码阅读器
每日杂事缠身,让自己在不断得烦扰之后终于有了自己的清静时光来熟悉一下我的工具,每次熟悉源码都需要先在windows端改好,拖到linux端,再编译.出现问题,还得重新回到windows端,这个过程太耗 ...
最新文章
- seo外链优化需要规避的那些坑
- 在IDEA中为项目引入maven中央仓库中的依赖包
- 通过Wireshark抓包分析谈谈DNS域名解析的那些事儿
- C#使用ping命令
- Configured broker.id 2 doesn‘t match stored broker.id 3 in meta.properties
- pcl_openmap_OpenMap教程第2部分–使用MapHandler构建基本地图应用程序–第1部分
- java命令行 引用jar包_java命令行引用jar包
- mysql实例怎么复制_Mysql实例MySQL数据库复制概论
- [恢]hdu 2317
- java 月度相减_java根据日期获取月龄,按照减法原理,先day相减,不够向month借;然后month相减,不够向year借;最后year相减。...
- 如何制作圆角布局..?
- 【基础】如何理解LSTM后接CRF?
- acm中c语言标准输入输出,ACM竞赛之输入输出
- Java使用ffmpeg将视频转为Mp4格式
- [日志]中国十大名花
- 视频编码器接入指挥调度平台的一种可行方法
- html自定义指针,如何自定义鼠标指针 怎样在wpf中自定义鼠标指针
- MS VC6 链接错误处理
- 微博数据分析工具限时福利!购买西瓜微数加送会员时长 ,最多加赠1个月!
- WPF 布局 - Grid, StackPanel, DockPanel, WrapPanel