Java中的阻塞队列-ArrayBlockingQueue(一)
最近在看一些java基础的东西,看到了队列这章,打算对复习的一些知识点做一个笔记,也算是对自己思路的一个整理,本章先聊聊java中的阻塞队列
参考文章:
http://ifeve.com/java-blocking-queue/
https://blog.csdn.net/u014082714/article/details/52215130
由上图可以用看出java中的阻塞队列都实现了 BlockingQueue接口,BlockingQueue又继承自Queue
1、什么是阻塞队列?
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
阻塞队列提供了四种处理方法:
- 抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。
- 返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null
- 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
- 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。
2.、Java里的阻塞队列
JDK7提供了7个阻塞队列。分别是
- ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
- DelayQueue:一个使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:一个不存储元素的阻塞队列。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
ArrayBlockingQueue
ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。我们可以使用以下代码创建一个公平的阻塞队列:
ArrayBlockingQueue fairQueue =
new
ArrayBlockingQueue(
1000
,
true
);
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) {
this(capacity, false);
} /**
* 构造函数。capacity设置数组大小 ,fair设置是否为公平锁
* capacity and the specified access policy.
*/
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);//是否为公平锁,如果是的话,那么先到的线程先获得锁对象。
//否则,由操作系统调度由哪个线程获得锁,一般为false,性能会比较高
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(); //要给数组设置内容,先上锁
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;//如果putIndex大于数组大小 ,那么从0重新开始
} finally {
lock.unlock();//最后一定要释放锁
}
}
关于方法的说明 /**
* 添加一个元素,其实super.add里面调用了offer方法
*/
public boolean add(E e) {
return super.add(e);
}
/**
* 当调用offer方法返回false时,直接抛出异常
*/
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
}
/**
*加入成功返回true,否则返回false
*
*/
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();//上锁
try {
if (count == items.length) //超过数组的容量
return false;
else {
enqueue(e); //放入元素
return true;
}
} finally {
lock.unlock();
}
}
/**
* 如果队列已满的话,就会等待
*/
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//和lock()方法的区别是让它在阻塞时也可抛出异常跳出
try {
while (count == items.length)
notFull.await(); //这里就是阻塞了,要注意。如果运行到这里,那么它会释放上面的锁,一直等到notify
enqueue(e);
} finally {
lock.unlock();
}
}
/**
* 带有超时时间的插入方法,unit表示是按秒、分、时哪一种
*/
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);//带有超时等待的阻塞方法
}
enqueue(e);//入队
return true;
} finally {
lock.unlock();
}
}
//实现的方法,如果当前队列为空,返回null
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
//实现的方法,如果当前队列为空,一直阻塞
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();//队列为空,阻塞方法
return dequeue();
} finally {
lock.unlock();
}
}
//带有超时时间的取元素方法,否则返回Null
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);//超时等待
}
return dequeue();//取得元素
} finally {
lock.unlock();
}
}
//只是看一个队列最前面的元素,取出是不删除队列中的原来元素。队列为空时返回null
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // 队列为空时返回null
} finally {
lock.unlock();
}
}
/**
* 返回队列当前元素个数
*
*/
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
/**
* 返回当前队列再放入多少个元素就满队
*/
public int remainingCapacity() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return items.length - count;
} finally {
lock.unlock();
}
}
/**
* 从队列中删除一个元素的方法。删除成功返回true,否则返回false
*/
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i])) {
removeAt(i); //真正删除的方法
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);//一直不断的循环取出来做判断
}
return false;
} finally {
lock.unlock();
}
}
/**
* 是否包含一个元素
*/
public boolean contains(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i]))
return true;
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
/**
* 清空队列
*
*/
public void clear() {
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int k = count;
if (k > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
items[i] = null;
if (++i == items.length)
i = 0;
} while (i != putIndex);
takeIndex = putIndex;
count = 0;
if (itrs != null)
itrs.queueIsEmpty();
for (; k > 0 && lock.hasWaiters(notFull); k--)
notFull.signal();
}
} finally {
lock.unlock();
}
}
/**
* 取出所有元素到集合
*/
public int drainTo(Collection<? super E> c) {
return drainTo(c, Integer.MAX_VALUE);
}
/**
* 取出所有元素到集合
*/
public int drainTo(Collection<? super E> c, int maxElements) {
checkNotNull(c);
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = Math.min(maxElements, count);
int take = takeIndex;
int i = 0;
try {
while (i < n) {
@SuppressWarnings("unchecked")
E x = (E) items[take];
c.add(x);
items[take] = null;
if (++take == items.length)
take = 0;
i++;
}
return n;
} finally {
// Restore invariants even if c.add() threw
if (i > 0) {
count -= i;
takeIndex = take;
if (itrs != null) {
if (count == 0)
itrs.queueIsEmpty();
else if (i > take)
itrs.takeIndexWrapped();
}
for (; i > 0 && lock.hasWaiters(notFull); i--)
notFull.signal();
}
}
} finally {
lock.unlock();
}
}
Java中的阻塞队列-ArrayBlockingQueue(一)的更多相关文章
- 聊聊并发(七)——Java中的阻塞队列
3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...
- Java中的阻塞队列(BlockingQueue)
1. 什么是阻塞队列 阻塞队列(BlockingQueue)是 Java 5 并发新特性中的内容,阻塞队列的接口是 java.util.concurrent.BlockingQueue,它提供了两个附 ...
- 多线程编程学习六(Java 中的阻塞队列).
介绍 阻塞队列(BlockingQueue)是指当队列满时,队列会阻塞插入元素的线程,直到队列不满:当队列空时,队列会阻塞获得元素的线程,直到队列变非空.阻塞队列就是生产者用来存放元素.消费者用来获取 ...
- 阻塞队列一——java中的阻塞队列
目录 阻塞队列简介:介绍阻塞队列的特性与应用场景 java中的阻塞队列:介绍java中实现的供开发者使用的阻塞队列 BlockQueue中方法:介绍阻塞队列的API接口 阻塞队列的实现原理:具体的例子 ...
- JUC之Java中的阻塞队列及其实现原理
在文章线程池实现原理 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中介绍了线程池的组成部分,其中一个组成部分就是阻塞队列.那么JAVA中的阻塞队列如何实现的呢? 阻塞队列,关键字是阻塞 ...
- Java中的阻塞队列
1. 什么是阻塞队列? 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列.这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用 ...
- java 中的阻塞队列
1.什么是阻塞队列: 支持阻塞的插入方法,意思是当队列满时,队列会阻塞插入元素的线程,知道队列不满. 支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空. 插入和移除操作的4种处 ...
- Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍
1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...
- java 5并发中的阻塞队列ArrayBlockingQueue的使用以及案例实现
演示一个阻塞队列的使用 public class BlockingQueueTest { public static void main(String[] args) { //创建一个包含三个元素的阻 ...
随机推荐
- Educational Codeforces Round 60 (Rated for Div. 2)D(思维,DP,快速幂)
#include <bits/stdc++.h>using namespace std;const long long mod = 1e9+7;unordered_map<long ...
- Vue2.0学习--Vue数据通信详解
一.前言 组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.组件间如何传递数据就显得至关重要.本文尽可能罗列出一些常见的数据传递方式,如p ...
- AB二进制
Description 若将一个正整数化为二进制数,在此二进制数中,我们将数字1的个数多于数字0的个数的这类二进制数称为A类数,否则就称其为B类数. 例如: (13)10=(1101)2 ...
- Oracle数据控制语言(DCL)
数据控制语言 用来授予或者回收访问数据库中的各种对象,如表 .视图 .索引 等 基本的操作如下: --创建用户 CREATE USER jim IDENTIFIED BY changeit; --给用 ...
- (Python OpenGL)【3】着色器 PyOpenGL
(Python OpenGL)现在开始我们使用着色器来进行渲染.着色器是目前做3D图形最流行的方式. OpenGL的渲染管线流程: 数据传输到OpenGL—>顶点处理器—>细分着色—> ...
- Qt 学习之路 2(14):对话框数据传递
Home / Qt 学习之路 2 / Qt 学习之路 2(14):对话框数据传递 Qt 学习之路 2(14):对话框数据传递 豆子 2012年9月15日 Qt 学习之路 2 53条评论 对话框 ...
- 树莓派使用 PPA 安装 Java 8
前言 在树莓派上安装 Java 8,与这篇的操作类似,不过树莓派不支持用 add-apt-repository 自动添加 webupd8team 的源,所以要手动添加. 步骤 在 /etc/apt/s ...
- Es6中Map对象和Set对象的介绍及应用
map和set的方法,工作中有使用到,所以学习一下: Map 对象 Map 对象保存键值对.任何值(对象或者原始值) 都可以作为一个键或一个值. var myMap = new Map(); myMa ...
- 电路中IC器件电压符号的解释
在电子芯片.运算处理器等集成电路行业中,存在多种电压.常用的的有:VDDQ->The supply voltage to output buffers of a memory chip 存储芯片 ...
- spoj COT - Count on a tree(主席树 +lca,树上第K大)
您将获得一个包含N个节点的树.树节点的编号从1到Ñ.每个节点都有一个整数权重. 我们会要求您执行以下操作: uvk:询问从节点u到节点v的路径上的第k个最小权重 输入 在第一行中有两个整数Ñ和中号.( ...