上一篇我们说了并发队列中的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. go使用错误概览

    1. 解决:GO语言中要提供给外面访问的方法或是结构体必须是首字母大写.这个结构体只有结构体名大写了,而里面的字段没有首字母大写,而GO语言在模板调用时应该认为是两个不同的过程,所以找不到值.于是把结 ...

  2. 动手实现CapsNet系列——1 概述

    Geoffrey Hinton是深度学习的开创者之一,反向传播等神经网络经典算法发明人,他在17年年底和他的团队发表了两篇论文,介绍了一种全新的神经网络,这种网络基于一种称为胶囊(Capsule)的结 ...

  3. 洛谷 P2058 海港(模拟)

    题目链接:https://www.luogu.com.cn/problem/P2058 这是一道用手写队列模拟的一道题,没有什么细节,只是注意因为数不会很大,所以直接用数作为数组下标即可,不用用map ...

  4. win10安装 .net3.5失败解决方法

    #开始 最近需要学习Sql Server 但是发现SQL Server2008r2 版本的安装程序基于.net 电脑没有安装.net3.5 #解决过程 可笑的是我在用离线安装包安装.net3.5的时候 ...

  5. Laravel Vuejs 实战:开发知乎 (4)实现找回密码

    资料 : Resetting Passwords   以及 Episode 35 - The Password Reset Flow 由于之前的实现里默认自带重置找回密码功能,不再复述. 默认的重置页 ...

  6. [USACO12JAN]牛联盟Bovine Alliance

    传送门:https://www.luogu.org/problemnew/show/P3043 其实这道题十分简单..看到大佬们在用tarjan缩点,并查集合并.... 蒟蒻渣渣禹都不会. 渣渣禹发现 ...

  7. 用for循环创建对象

    以下代码Demo: public class TestDemo { public static void main(String[] args) { //以创建5个student为例 int coun ...

  8. C语言:利用指针解决:统计一个长度为2的字符串在另外一个字符串中出现的次数。

    //统计一个长度为2的字符串在另外一个字符串中出现的次数. #include <conio.h> #include <stdio.h> #include <string. ...

  9. 忘记SYS密码

    进入控制台录入   sqlplus /nolog; connect / as sysdba alter user sys identified by ; alter user system ident ...

  10. JavaScript的变量提升机制

    变量提升 JavaScript的变量提升有两种,用var声明的变量以及用function声明的变量. 用var声明的变量 我们先来看下面这段代码,a的值是多少 代码1 console.log(a); ...