【Java数据结构及算法实战】系列008:Java队列02——阻塞队列BlockingQueue

简介: 阻塞队列(BlockingQueue)是一种支持额外操作的队列

阻塞队列(BlockingQueue)是一种支持额外操作的队列,这两个附加的操作是:

l 在队列为空时,获取元素的线程会等待队列变为非空。

l 当队列满时,存储元素的线程会等待队列可用。

Java提供了java.util.concurrent.BlockingQueue接口以提供对阻塞队列的支持。该接口是Java Collections Framework的一个成员。

  1. BlockingQueue的方法

BlockingQueue对不能立即满足的操作有不同的处理方法,这些方法共有四种形式:

l 抛出一个异常

l 返回一个特殊值,根据操作的不同可能返回null或false

l 无限期地阻塞当前线程,直到操作成功

l 在放弃之前只阻塞给定的最大时间限制。

这些方法总结如下:

方法\处理方式

抛出异常

返回特殊值

一直阻塞

超时退出

插入方法

add(e)

队列未满时,返回 true;

队列满则抛出异常

offer(e)

队列未满时,返回 true;

队列满时返回 false。非阻塞立即返回。

put(e)

队列未满时,直接插入没有返回值;

队列满时会阻塞等待,一直等到队列未满时再插入。

offer(e,time,unit)

设定等待的时间,如果在指定时间内还不能往队列中插入数据则返回 false。

插入成功返回 true。

移除方法

remove()

队列不为空时,返回队首值并移除

队列为空时抛出异常

poll()

队列不为空时返回队首值并移除

队列为空时返回 null。非阻塞立即返回。

take()

队列不为空返回队首值并移除

当队列为空时会阻塞等待,一直等到队列不为空时再返回队首值。

poll(time,unit)

设定等待的时间,如果在指定时间内队列还未孔则返回 null

不为空则返回队首值

检查方法

element()

队列不为空时返回队首值但不移除

队列为空时抛出异常。

peek()

队列不为空时返回队首值但不移除

队列为空时返回 null。

不可用

不可用

BlockingQueue不接受null值,当尝试add、put或offer一个null值时,将抛出NullPointerException。null值有特殊的用意,用来表示poll操作失败。

BlockingQueue可以有容量限制,如果不指定容量则默认容量大小是Integer.MAX_VALUE。任何时候,只要超过了剩余容量(remainingCapacity),则新的元素不能被放入队列中。

BlockingQueue实现被设计为主要用于生产者-消费者队列,但还支持Collection接口。因此,例如,使用remove(x)可以从队列中删除任意元素。然而,这些操作通常不会非常有效地执行,并且只用于偶尔使用,例如在取消排队的消息的时候。

BlockingQueue实现是线程安全的。所有排队方法都使用内部锁或其他并发控制形式自动地实现其效果。但是,除非在实现中指定了otherwise,否则批量Collection操作addAll、containsAll、retainAll和removeAll不一定是自动执行的。因此,例如,在只添加c中的某些元素之后, addAll ( c )可能会失败(抛出一个异常)。

BlockingQueue本质上不支持任何类型的close或shutdown操作,以指示不再添加任何项。这些特性的需求和使用往往依赖于具体的实现。例如,一种常见的策略是生产者插入特殊的对象(比如end-of-stream或 poison),当被消费者采用时,这些对象将得到相应的解释。

  1. BlockingQueue的使用示例

以下是一个典型的生产者-消费者场景的使用示例。注意,一个BlockingQueue可以安全地与多个生产者和多个消费者一起使用。

class Producer implements Runnable {

private final BlockingQueue queue;

Producer(BlockingQueue q) { queue = q; }

public void run() {

 try {



   while (true) { queue.put(produce()); }



 } catch (InterruptedException ex) { ... handle ...}


}

Object produce() { ... }

}

class Consumer implements Runnable {

private final BlockingQueue queue;

Consumer(BlockingQueue q) { queue = q; }

public void run() {

 try {



   while (true) { consume(queue.take()); }



 } catch (InterruptedException ex) { ... handle ...}


}

void consume(Object x) { ... }

}

class Setup {

void main() {

 BlockingQueue q = new SomeQueueImplementation();



 Producer p = new Producer(q);



 Consumer c1 = new Consumer(q);



 Consumer c2 = new Consumer(q);



 new Thread(p).start();



 new Thread(c1).start();



 new Thread(c2).start();


}

}}

  1. BlockingQueue的内存一致性影响

这种影响与其他并发集合一样,某个线程将数据元素放入BlockingQueue的操作,happen-before 于另一个线程访问或者删除BlockingQueue中该数据元素的操作。

3.1. happens-before的含义

happen-before规则用来描述两个操作之间的顺序关系,这两个操作可以再一个线程内,也可以不再一个线程内。此顺序并不严格意味着执行时间上的顺序,而是至前一个操作的结果要对后一个操作可见。

happens-Before关系的定义如下:

l 如果一个happens-before另一个操作,那么第一个操作的执行结果对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前

l 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。

举例来说,如果在程序执行顺序上,A先于B,并且A修改了共享变量,而B正好使用该共享变量,那么A需要happen-before B,再直白一点,就是A对共享变量的修改,需要在B执行时,对B可见。

3.2. happens-before规则

l 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。

l 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。

l volatile规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。

l 传递性:如果A happens-before B,并且B happens-before C,那么A happens-before C。

l start()规则:如果线程A执行操作ThreadB.start(),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。

l join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

对所有这些规则的说明:A happens-before B并不意味着A一定要先在B之前发生,而是说,如果A已经发生在了B前面,那么A的操作结果一定要对B可见

  1. 参考引用

本系列归档至《Java数据结构及算法实战》:https://github.com/waylau/java-data-structures-and-algorithms-in-action
《数据结构和算法基础(Java语言实现)》(柳伟卫著,北京大学出版社出版):https://item.jd.com/13014179.html

目录
相关文章
|
18天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
29 1
|
21天前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
66 4
|
19天前
|
存储 算法 搜索推荐
Python 中数据结构和算法的关系
数据结构是算法的载体,算法是对数据结构的操作和运用。它们共同构成了计算机程序的核心,对于提高程序的质量和性能具有至关重要的作用
|
19天前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
27天前
|
算法
数据结构之路由表查找算法(深度优先搜索和宽度优先搜索)
在网络通信中,路由表用于指导数据包的传输路径。本文介绍了两种常用的路由表查找算法——深度优先算法(DFS)和宽度优先算法(BFS)。DFS使用栈实现,适合路径问题;BFS使用队列,保证找到最短路径。两者均能有效查找路由信息,但适用场景不同,需根据具体需求选择。文中还提供了这两种算法的核心代码及测试结果,验证了算法的有效性。
89 23
|
27天前
|
算法
数据结构之蜜蜂算法
蜜蜂算法是一种受蜜蜂觅食行为启发的优化算法,通过模拟蜜蜂的群体智能来解决优化问题。本文介绍了蜜蜂算法的基本原理、数据结构设计、核心代码实现及算法优缺点。算法通过迭代更新蜜蜂位置,逐步优化适应度,最终找到问题的最优解。代码实现了单链表结构,用于管理蜜蜂节点,并通过适应度计算、节点移动等操作实现算法的核心功能。蜜蜂算法具有全局寻优能力强、参数设置简单等优点,但也存在对初始化参数敏感、计算复杂度高等缺点。
58 20
|
18天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
48 1
|
27天前
|
机器学习/深度学习 算法 C++
数据结构之鲸鱼算法
鲸鱼算法(Whale Optimization Algorithm,WOA)是由伊朗研究员Seyedali Mirjalili于2016年提出的一种基于群体智能的全局优化算法,灵感源自鲸鱼捕食时的群体协作行为。该算法通过模拟鲸鱼的围捕猎物和喷出气泡网的行为,结合全局搜索和局部搜索策略,有效解决了复杂问题的优化需求。其应用广泛,涵盖函数优化、机器学习、图像处理等领域。鲸鱼算法以其简单直观的特点,成为初学者友好型的优化工具,但同时也存在参数敏感、可能陷入局部最优等问题。提供的C++代码示例展示了算法的基本实现和运行过程。
47 0
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
27天前
|
算法 vr&ar 计算机视觉
数据结构之洪水填充算法(DFS)
洪水填充算法是一种基于深度优先搜索(DFS)的图像处理技术,主要用于区域填充和图像分割。通过递归或栈的方式探索图像中的连通区域并进行颜色替换。本文介绍了算法的基本原理、数据结构设计(如链表和栈)、核心代码实现及应用实例,展示了算法在图像编辑等领域的高效性和灵活性。同时,文中也讨论了算法的优缺点,如实现简单但可能存在堆栈溢出的风险等。
39 0