上一篇我们说了并发队列中的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. 【安卓逆向】ARM常见汇编指令总结

    跳转指令 B 无条件跳转 BL 带链接的无条件跳转 BX 带状态切换的无条件跳转 BLX 带链接和状态的无条件跳转 存储器与寄存器交互数据指令(核心) 存储器:主存和内存 寄存器中放的数据:可以是字符 ...

  2. 2.5 【配置环境】多浏览器驱动 (chrome、IE、Firefox)❀

    来源:http://blog.csdn.net/huilan_same/article/details/51896672 http://www.cnblogs.com/thinkCoding/p/64 ...

  3. 莫队或权值线段树 或主席树 p4137

    题目描述 有一个长度为n的数组{a1,a2,…,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. 输入格式 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问l,r. 输出格式 ...

  4. 干了这杯Java,让你的Idea比eclipse好用

    1.Idea基本配置 1.1 Idea简介 Idea是一个专门针对Java的集成开发工具(IDE),由Java语言编写.所以,需要有JRE运行环境并配置好环境变量.简单的说,Idea是写代码用的工具. ...

  5. javaScript中的toFix(n)方法

    定义和用法 toFixed() 方法可把 Number 四舍五入为指定小数位数的数字. 语法 NumberObject.toFixed(num) 返回值 返回 NumberObject 的字符串表示, ...

  6. java spring-boot 服务器启动参数设置

    java -jar -Xms5866m -Xmx5866m -Xss256k -Xloggc:/home/work/spring-boot/logs/gc-%t.log -XX:+UseGCLogFi ...

  7. 本地mongodb数据库导出到远程数据库中

    把本地Mongodb中的数据导入(批量插入)到服务器的数据库中 1.导出数据: mongoexport -d admin -c users -o outdatafile.dat 选项解释: -d 指明 ...

  8. 服务器部署网站后,公网ip可以访问,域名不能访问问题(稳)

    出现问题 这几天我网站已经部署到vps上,域名也备好案,想使用域名指向我们公网ip.指完发现用域名访问不了网站,但是公网ip可以.于是看了网上资料,好像是要清除浏览器DNS缓存,我清完没用.然后发现我 ...

  9. ubuntu14.04安装好Hadoo之后接着安装hbase和介绍常用命令

    1.解压 tar -zxvf hbase-1.0.0-bin.tar.gzsudo mv hbase-1.0.0 /opt/hbasecd /optsudo chmod -R 775 hbase 2. ...

  10. 喵星之旅-狂奔的兔子-docker安装和基本使用

      一.前提条件 目前,CentOS 仅发行版本中的内核支持 Docker. 位.系统内核版本为 3.10 以上. 位系统.参考喵星之旅-狂奔的兔子-linux安装 二.CentOS 7下安装 Doc ...