阻塞队列一——java中的阻塞队列
目录
- 阻塞队列简介:介绍阻塞队列的特性与应用场景
- java中的阻塞队列:介绍java中实现的供开发者使用的阻塞队列
BlockQueue中方法:介绍阻塞队列的API接口- 阻塞队列的实现原理:具体的例子说明阻塞队列的实现原理
- 总结
阻塞队列简介
阻塞队列(BlockingQueue)首先是一个支持先进先出的队列,与普通的队列完全相同;
其次是一个支持阻塞操作的队列,即:
- 当队列满时,会阻塞执行插入操作的线程,直到队列不满。
- 当队列为空时,会阻塞执行获取操作的线程,直到队列不为空。
阻塞队列用在多线程的场景下,因此阻塞队列使用了锁机制来保证同步,这里使用的可重入锁;
而对于阻塞与唤醒机制则有与锁绑定的Condition实现
应用场景:生产者消费者模式
java中的阻塞队列
java中的阻塞队列根据容量可以分为有界队列和无界队列:
- 有界队列:队列中只能存储有限个元素,超出后存放元素线程会被阻塞或者失败。
- 无界队列:队列中可以存储无限个元素。
java8中提供了7种阻塞队列阻塞队列供开发者使用,如下表:
| 类名 | 描述 |
|---|---|
| ArrayBlockingQueue | 一个由数组结构组成的有界阻塞队列 |
| LinkedBlockingQueue | 由链表结构组成的有界阻塞队列(默认大小Integer.MAX_VALUE) |
| PriorityBlockingQueue | 支持优先级排序的无界阻塞队列 |
| DelayQueue | 使用优先级队列实现的延迟无界阻塞队列 |
| SynchronousQueue | 不存储元素的阻塞队列,即单个元素的队列 |
| LinkedTransferQueue | 由链表结构组成的无界阻塞队列 |
| LinkedBlockingDeque | 由链表结构组成的双向阻塞队列 |
另外还有一个在ScheduledThreadPoolExecutor中实现的DelayedWorkQueue阻塞队列,
但这个阻塞队列开发者不能使用。它们之间的UML类图如下图:

BlockingQueue接口是阻塞队列对外的访问接口,所有的阻塞队列都实现了BlockQueue中的方法
BlockQueue中方法
作为一个队列的核心方法就是入队和出队。由于存在阻塞策略,BlockQueue将出队入队的情况分为了四组,每组提供不同的方法:
抛出异常:当队列满时,如果再往队列中插入元素,则抛出
IllegalStateException异常;
当队列为空时,从队列中获取元素则抛出NoSuchElementException异常。返回特定值(布尔值):当队列满时,如果再往队列中插入元素,则返回false;当队列为空时,从队列中获取元素则返回null。
一直阻塞:当队列满时,如果再往队列中插入元素,阻塞当前线程直到队列中至少一个被移除或者响应中断退出;
当队列为空时,则阻塞当前线程直到至少一个元素元素入队或者响应中断退出。超时退出:当队列满时,如果再往队列中插入元素,阻塞当前线程直到队列中至少一个被移除或者达到指定的等待时间退出或者响应中断退出;
当队列为空时,则阻塞当前线程直到至少一个元素元素入队或者达到指定的等待时间退出或者响应中断退出。
对于每种情况BlockingQueue提供的方法如下表:
| 方法\处理方式 | 抛出异常 | 返回特定值(布尔值) | 一直阻塞 | 超时退出 |
|---|---|---|---|---|
| 插入 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
| 移除 | remove() | poll() | take() | poll(time.unit) |
| 检查 | element() | peek() | 不可用 | 不可用 |
上述方法一般用于生产者-消费者模型中,是其中的生产和消费操作队列的核心方法。
除了这些方法,BlockingQueue还提供了一些其他的方法如下表:
| 方法名称 | 描述 |
|---|---|
| remove(Object o) | 从队列中移除一个指定值 |
| size() | 获取队列中元素的个数 |
| contains(Object o) | 判断队列是否包含指定的元素,但是这个元素在这次判断完可能就会被消费 |
| drainTo(Collection<? super E> c) | 将队列中元素放在给定的集合中,并返回添加的元素个数 |
| drainTo(Collection<? super E> c, int maxElements) | 将队列中元素取maxElements(不超过队列中元素个数)个放在给定的集合中,并返回添加的元素个数 |
| remainingCapacity() | 计算队列中还可以存放的元素个数 |
| toArray() | 以objetc数组的形式获取队列中所有的元素 |
| toArray(T[] a) | 以给定类型数组的方式获取队列中所有的元素 |
| clear() | 清空队列,危险的操作 |
阻塞队列的实现原理
阻塞队列的实现依靠通知模式实现:当生产者向满了的队列中添加元素时,会阻塞住生产者,
直到消费者消费了一个队列中的元素后会通知消费者队列可用,此时再由生产者向队列中添加元素。反之亦然。
阻塞队列的阻塞唤醒依靠Condition——条件队列来实现。
以ArrayBlockingQueue为例说明:
ArrayBlockingQueue的定义:
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/** The queued items */
//以数组的结构存储队列的元素,采用的是循环数组
final Object[] items;
/** items index for next take, poll, peek or remove */
//队列的队头索引
int takeIndex;
/** items index for next put, offer, or add */
//队列的队尾索引
int putIndex;
/** Number of elements in the queue */
//队列中元素的个数
int count;
/** Main lock guarding all access */
//对于ArrayBlockingQueue所有的操作都需要加锁,
final ReentrantLock lock;
/** Condition for waiting takes */
//条件队列,当队列为空时阻塞消费者并在生产者生产后唤醒消费者
private final Condition notEmpty;
/** Condition for waiting puts */
//条件队列,当队列满时阻塞生产者,并在消费者消费队列后唤醒生产者
private final Condition notFull;
}
根据类的定义字段可以看到,有两个Condition条件队列,猜测以下过程
- 当队列为空,消费者试图消费时应该调用
notEmpty.await()方法阻塞,并在生产者生产后调用notEmpty.single()方法 - 当队列已满,生产者试图放入元素应调用
notFull.await()方法阻塞,并在消费者消费队列后调用notFull.single()方法
向队列中添加元素put()方法的添加过程。
/**
* 向队列中添加元素
* 当队列已满时需要阻塞当前线程
* 放入元素后唤醒因队列为空阻塞的消费者
*/
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//当队列已满时需要notFull.await()阻塞当前线程
//offer(e,time,unit)方法就是阻塞的时候加了超时设定
while (count == items.length)
notFull.await();
//放入元素的过程
enqueue(e);
} finally {
lock.unlock();
}
}
/**enqueue实际添加元素的方法*/
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
//如果条件队列中存在等待的线程
//唤醒
notEmpty.signal();
}
从队列中获取元素take()方法的获取过程。
/**
* 从队列中获取元素
* 当队列已空时阻塞当前线程
* 从队列中消费元素后唤醒等待的生产线程
*/
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//队列为空需要阻塞当前线程
while (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];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
//消费元素后从唤醒阻塞的生产者线程
notFull.signal();
return x;
}
总结
阻塞队列提供了不同于普通队列的增加、删除元素的方法,核心在与队列满时阻塞生产者和队列空时阻塞消费者。
这一阻塞过程依靠与锁绑定的Condition对象实现。Condition接口的实现在AQS中实现,具体的实现类是
ConditionObject
阻塞队列一——java中的阻塞队列的更多相关文章
- 聊聊并发(七)——Java中的阻塞队列
3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...
- Java中的阻塞队列(BlockingQueue)
1. 什么是阻塞队列 阻塞队列(BlockingQueue)是 Java 5 并发新特性中的内容,阻塞队列的接口是 java.util.concurrent.BlockingQueue,它提供了两个附 ...
- Java中的阻塞队列-ArrayBlockingQueue(一)
最近在看一些java基础的东西,看到了队列这章,打算对复习的一些知识点做一个笔记,也算是对自己思路的一个整理,本章先聊聊java中的阻塞队列 参考文章: http://ifeve.com/java-b ...
- 多线程编程学习六(Java 中的阻塞队列).
介绍 阻塞队列(BlockingQueue)是指当队列满时,队列会阻塞插入元素的线程,直到队列不满:当队列空时,队列会阻塞获得元素的线程,直到队列变非空.阻塞队列就是生产者用来存放元素.消费者用来获取 ...
- JUC之Java中的阻塞队列及其实现原理
在文章线程池实现原理 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中介绍了线程池的组成部分,其中一个组成部分就是阻塞队列.那么JAVA中的阻塞队列如何实现的呢? 阻塞队列,关键字是阻塞 ...
- AQS源码深入分析之条件队列-你知道Java中的阻塞队列是如何实现的吗?
本文基于JDK-8u261源码分析 1 简介 因为CLH队列中的线程,什么线程获取到锁,什么线程进入队列排队,什么线程释放锁,这些都是不受我们控制的.所以条件队列的出现为我们提供了主动式地.只有满足指 ...
- Java中的阻塞和非阻塞IO包各自的优劣思考(经典)
Java中的阻塞和非阻塞IO包各自的优劣思考 NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式. 反应器(Reactor):用于事件多路分离和分派的体系结构模式 通常的,对一个 ...
- Java中的阻塞队列
1. 什么是阻塞队列? 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列.这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用 ...
- java 中的阻塞队列
1.什么是阻塞队列: 支持阻塞的插入方法,意思是当队列满时,队列会阻塞插入元素的线程,知道队列不满. 支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空. 插入和移除操作的4种处 ...
随机推荐
- 「雕爷学编程」Arduino动手做(18)---太阳能电池模块
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...
- flask之Blueprint蓝图
flask_Blueprint.py ''' flask中的Blueprint蓝图: (1)新建模块,例如Bp1.py,Bp2.py,在模块中创建蓝图实例 (2)通过app.register_blue ...
- 机器学习决策树ID3算法,手把手教你用Python实现
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第21篇文章,我们一起来看一个新的模型--决策树. 决策树的定义 决策树是我本人非常喜欢的机器学习模型,非常直观容易理解 ...
- 枚举 转化为可行性判定问题 网络流 poj3189
Steady Cow Assignment Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6914 Accepted: ...
- A+B Coming(hdu1720)
思考:十六进制的输入->%x,定义时用int.要变成十进制输出,直接在输出时用->%d. #include<stdio.h> int main() { int A,B; cha ...
- 当 RocketMQ 遇上 Serverless,会碰撞出怎样的火花?
作者 | 元毅 阿里巴巴高级开发工程师 阿里巴巴云原生公众号后台回复 Knative,免费下载<Knative 云原生应用开发指南>电子书! 想必大家都比较了解 RocketMQ 消息服 ...
- Linux centos 7 目录结构
一.目录结构与用途: /boot:系统引导文件.内核 /bin:用户的基本命令 /dev:设备文件 /etc:配置文件 /home:用户目录 /root:root用户目录 /sbin:管理类的基本命令 ...
- WordPress 安全配置
关闭后台主题编辑功能 WordPress后台的主题一旦权限开放就可以在后台直接编辑,如果没有开放则只可浏览.主机若有安装suPHP默认就是可以编辑.如果你觉得这项功能用不到,建议您可以关闭它,毕竟直接 ...
- vim的基础命令
:q 退出 :wq 保存并退出 :q! 不保存并退出 :w 保存 :w! 强行保存
- 【JUC】synchronizated和lock的区别&新lock的优势
原始构成 synchronized是关键字,属于JVM层面 javap -c 的结果显示 synchronized是可重入锁 11:是正常退出 17:是异常退出[保证不产生死锁和底层故障] Lock是 ...