上一篇我们说了并发队列中的LinkedBlockingQueue队列,这次我们看看ArrayBlockingQueue,看看名字,我们想象一下LinkedList和ArrayList的区别,我们可以知道ArrayBlockingQueue底层肯定是基于数组实现的,这是一个有界数组;

  ArrayBlockingQueue其中的组成部分和LinkedBlockingQueue及其相似,也是有两个条件变量,维护阻塞队列,实现了生产消费者模式;

一.简单认识ArrayBlockingQueue

  先看看几个常用属性:

//数组用于存放队列元素
final Object[] items;
//出队索引
int takeIndex;
//入队索引
int putIndex;
//队列中元素数量
int count;
//独占锁
final ReentrantLock lock;
//如果数组中为空,还有线程取数据,就丢到这个条件变量中来阻塞
private final Condition notEmpty;
//队列满了,还有线程往数组中添加数据,就把线程丢到这里来阻塞
private final Condition notFull;

  由于这是一个有界的数组,我们再看看构造器:

//指定容量,默认是非公平策略
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
//指定容量和独占锁的策略
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();
}
//可以指定容量,锁的策略,还有初始化数据
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair); final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
int i = 0;
try {
for (E e : c) {
checkNotNull(e);
items[i++] = e;
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
count = i;
putIndex = (i == capacity) ? 0 : i;
} finally {
lock.unlock();
}
}

二.offer方法

  向队列尾部添加一个元素,添加成功就返回true,队列满了就丢掉当前元素直接返回false,方法不阻塞;

public boolean offer(E e) {
//非空检验
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
//如果数组中实际数量和最大容量相等,添加失败,返回false
if (count == items.length)
return false;
else {
//添加成功,方法实现在下面
enqueue(e);
return true;
}
} finally {
//释放锁
lock.unlock();
}
}
private void enqueue(E x) {
//拿到数组
final Object[] items = this.items;
//在putIndex这个位置放入数据x,然后把putIndex加一,说明这个参数表示的是下一个数据要放入的位置的索引
items[putIndex] = x;
//这里putIndex是先加一然后再比较是否相等,比如这里数组的最大容量是5,那么索引的最大值应该是4,而如果putIndex等于5了,说明数组
//越界了,加把这个索引重置为0
if (++putIndex == items.length)
putIndex = 0;
count++;
//添加完成之后,说明了数组中有数据了,这里会唤醒之前因为去数组中取数据而阻塞的线程
notEmpty.signal();
}

三.put方法

  向队列尾部插入一个元素,队列有空闲就插入成功返回true,队列满了就阻塞当前线程到notFull的条件队列中,等有空闲之后就会被唤醒;阻塞过程中对中断会有响应的;

public void put(E e) throws InterruptedException {
//非空检查
checkNotNull(e);
final ReentrantLock lock = this.lock;
//注意该锁的获取方式
lock.lockInterruptibly();
try {
//如果线程满了,就把当前线程放到notFull条件变量的阻塞队列中
while (count == items.length)
notFull.await();
//没有满,就添加数据
enqueue(e);
} finally {
//释放锁
lock.unlock();
}
}

四.poll方法

  头部获取并移除一个元素,如果队列为空,就返回null,方法不阻塞;

public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//如果队列为空,就返回null
//如果队列不为空,就调用dequeue方法获取并删除队列头部的元素
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
//获取数组
final Object[] items = this.items;
@SuppressWarnings("unchecked")
//获取takeIndex位置的元素,最后会将这个返回
E x = (E) items[takeIndex];
//然后将takeInde位置置为空
items[takeIndex] = null;
//如果takeIndex已经是数组的最后一个位置了,就将takeIndex重置为0
if (++takeIndex == items.length)
takeIndex = 0;
//实际数量减一
count--;
if (itrs != null)
itrs.elementDequeued();
//唤醒notFull中线程
notFull.signal();
return x;
}

五.take方法

  获取并删除当前队列头部的元素,如果队列为空当前线程阻塞直到被唤醒,对中断有响应;

 public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//可中断的方式获取锁
lock.lockInterruptibly();
try {
//如果数组为空,此时就唤醒notEmpty中条件队列里的线程
while (count == 0)
notEmpty.await();
//获取并删除头节点
return dequeue();
} finally {
lock.unlock();
}
}

六.peek方法

  只是获取头部元素,不删除,如果队列为空就返回null,这个方法是线程不阻塞的

public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // null when queue is empty
} finally {
lock.unlock();
}
} //获取到数组中索引为takeIndex中的数据
@SuppressWarnings("unchecked")
final E itemAt(int i) {
return (E) items[i];
}

七.总结

  理解了上一篇博客中说的LinkedBlockingQueue,那么再看这一篇其实太容易了,就是操作数组嘛!用下面这个图表示:

并发队列之ArrayBlockingQueue的更多相关文章

  1. 并发队列:ArrayBlockingQueue实际运用场景和原理

    ArrayBlockingQueue实际应用场景 之前在某公司做过一款情绪识别的系统,这套系统通过调用摄像头接口采集人脸信息,将采集的人脸信息做人脸识别和情绪分析,最终经过一定的算法将个人情绪数据转化 ...

  2. 10.并发包阻塞队列之ArrayBlockingQueue

    上一节中对并发包中的非阻塞队列ConcurrentLinkedQueue的入队.出队做了一个简要的分析,本文将对并发包中的阻塞队列做一个简要分析. Java并发包中的阻塞队列一共7个,当然他们都是线程 ...

  3. 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析

    LinkedBlockingQueue 在看源码之前,通过查询API发现对LinkedBlockingQueue特点的简单介绍: 1.LinkedBlockingQueue是一个由链表实现的有界队列阻 ...

  4. 并发队列ConcurrentLinkedQueue、阻塞队列AraayBlockingQueue、阻塞队列LinkedBlockingQueue 区别和使用场景总结

      三者区别与联系: 联系,三者 都是线程安全的.区别,就是 并发  和 阻塞,前者为并发队列,因为采用cas算法,所以能够高并发的处理:后2者采用锁机制,所以是阻塞的.注意点就是前者由于采用cas算 ...

  5. 并发包阻塞队列之ArrayBlockingQueue

    并发包阻塞队列之ArrayBlockingQueue   jdk1.7.0_79  上一节中对并发包中的非阻塞队列ConcurrentLinkedQueue的入队.出队做了一个简要的分析,本文将对并发 ...

  6. JAVA多线程(二) 并发队列和阻塞队列

    github代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service/ ...

  7. Java深入学习(2):并发队列

    并发队列: 在并发队列中,JDK有两套实现: ConcurrentLinkedQueue:非阻塞式队列 BlockingQueue:阻塞式队列 阻塞式队列非阻塞式队列的区别: 阻塞式队列入列操作的时候 ...

  8. 【Java并发】并发队列与线程池

    并发队列 阻塞队列与非阻塞队 ConcurrentLinkedQueue BlockingQueue ArrayBlockingQueue LinkedBlockingQueue PriorityBl ...

  9. 并发队列 ConcurrentLinkedQueue 及 BlockingQueue 接口实现的四种队列

    队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列. 在队列这 ...

随机推荐

  1. Bugku-CTF加密篇之进制转换(二进制、八进制、十进制、十六进制,你能分的清吗?)

    进制转换 二进制.八进制.十进制.十六进制,你能分的清吗?

  2. AcWing 852. spfa判断负环 边权可能为负数。

    #include <cstring> #include <iostream> #include <algorithm> #include <queue> ...

  3. Chrom Develop Extensions

    Chrome插件 Extensions are small software programs that customize the browsing experience. They enable ...

  4. 吴裕雄 python 机器学习——数据预处理标准化StandardScaler模型

    from sklearn.preprocessing import StandardScaler #数据预处理标准化StandardScaler模型 def test_StandardScaler() ...

  5. vue音乐播放器

    利用vue写一个简单的音乐播放器,包括功能有歌曲搜索.歌曲播放.歌曲封面.歌曲评论.播放动画.mv播放六个功能. <template> <div class="wrap&q ...

  6. 寒假安卓app开发学习记录(5)

    第一个项目运行成功! 今天主要是看视频 ,学习Android项目的目录结构.大概学习了一个小时左右.然后在网上学习和对比eclipse上的目录,了解每个目录的功能是什么. 了解了Android项目的目 ...

  7. 每天进步一点点------Altium Designer Rules规则详解

    对于PCB的设计, AD提供了详尽的10种不同的设计规则,这些设计规则则包括导线放置.导线布线方法.元件放置.布线规则.元件移动和信号完整性等规则.根据这些规则, Protel DXP进行自动布局和自 ...

  8. flask 2 进阶

    # 创建项目 jinja2 语法基础 # pycharm 里面 创建 new project -->pure python 之后选择路径 选择解释器 以及虚拟环境问题 from flask im ...

  9. webRTC中回声消除(AEC)模块编译时aec_rdft.c文件报错:

    webRTC中回声消除(AEC)模块编译时aec_rdft.c文件报错. 原因是: 局部变量ip跟全局变量冲突的问题,可以将局部变量重新命名一下,就可以通过编译了. aec_rdft.c修改以后文件代 ...

  10. 国际化支持、activity生命周期、屏幕翻转的ui适配

    国际化 对于手机的不同语言做出不同的语言描述,这里只是简单的提一下,实际上针对比较大型的项目,有可能不同的语言要做不同的ui适配. 例如下面:中文长度不长但是在德语中占据很长的位置,这个就要针对性的适 ...