JUC--阻塞队列

目录

问题引出

 一.单端阻塞队列(BlockingQueue)

二.双端阻塞队列(BlockingDeque)

三.延迟队列(DelayQueue)


问题引出

由于实现消费者-生产者模型,每一次实现都比较麻烦,比如sychronized的同步处理,或者通过锁实现。这些实现起来都比较繁琐,为了简单就能实现这种模型,JUC提供了阻塞队列接口:BlockingQueue(单端阻塞队列)和BlockingDeque(双端阻塞队列)

 一.单端阻塞队列(BlockingQueue)

原理:通过使用FIFO模式处理的集合结构

什么是FIFO?

FIFO(First-In, First-Out)是一种常见的处理数据的方式,也被称为先进先出模式。在FIFO模式中,首先进入队列的数据首先被处理,而最后进入队列的数据最后被处理。

可以将FIFO模式理解为排队等候的情景,比如在超市的收银台,顾客按照先后顺序排队结账。当一个顾客结完账离开后,下一个顾客才能开始结账。这就是FIFO模式的处理顺序。

在计算机科学中,FIFO模式通常用于数据缓冲区、队列和调度算法等场景。例如,在操作系统中,进程调度算法可以使用FIFO模式,根据进程到达的先后顺序来决定执行顺序;在网络通信中,消息队列可以使用FIFO模式确保消息按照发送的先后顺序被接收和处理。

单端阻塞队列BlockQueue的常用方法:

方法描述
put(item)将指定的项放入队列中,如果队列已满则阻塞,直到有空间可用
take()从队列中获取并移除一个项,如果队列为空则阻塞,直到有项可取
offer(item)尝试将指定的项放入队列中,如果队列已满则立即返回false,否则返回true
poll(timeout)从队列中获取并移除一个项,在指定的超时时间内如果队列为空则返回null
peek()返回队列中的第一个项,但不对队列进行修改,如果队列为空则返回null
size()返回队列中当前的项数
isEmpty()检查队列是否为空
isFull()检查队列是否已满
clear()清空队列,移除所有的项

单端阻塞队列接口BlockingQueue提供多个子类ArrayBlockingQueue(数组结构)、LinkedBlockingQueue(链表单端阻塞队列)、PriorityBlockingQueue(优先级阻塞队列)、SynchronousQueue(同步队列)

ArrayBlockingQueue

案例代码:

上述代码修改后如下

package Example2129;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args){
//        创建对象和资源量BlockingQueue<String> queue  = new ArrayBlockingQueue<String>(2);
// 创建一个包含各种美食的String数组String[] foods = {"披萨","汉堡", "寿司", "墨西哥炸玉米卷", "牛排", "意大利面", "烤鸭", "富士山寿司", "印度咖喱", "巴西烤肉",};int id;
//        两个厨师for (int i=0;i<2;i++){id = i;new Thread(()->{for (int j=0;j<10;j++){try {
//                        模拟做菜时间TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+"已经做完菜肴"+foods[j]+"并端上座子");queue.put(foods[j]);}catch (Exception e){e.printStackTrace();}}},"厨师"+id).start();}for (int i=0;i<10;i++){id = i;new Thread(()->{for (int j=0;j<2;j++){try {
//                        模拟客人吃饭的时间TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+"享用完"+queue.take()+"这道菜");}catch (Exception e){e.printStackTrace();}}},"客人"+i).start();}}
}

注意:阻塞队列虽然解决了数据存满则线程等待的情况,但是并没有解决线程并发的问题

LinkedBlockingQueue

案例代码:

package Example2130;import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {
//        设置容量BlockingQueue<String> queue = new LinkedBlockingQueue<>(2);Random random = new Random();new Thread(()->{while (true){try {if (queue.size()==2){System.out.println("队列已满");TimeUnit.SECONDS.sleep(1);}else {TimeUnit.SECONDS.sleep(random.nextInt(3));System.out.println("存入数据");queue.put("存入数据");}} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();new Thread(()->{while (true){try {TimeUnit.SECONDS.sleep(random.nextInt(3));if (queue.isEmpty()){System.out.println("队列空了啊");TimeUnit.SECONDS.sleep(1);}else {System.out.println("取出数据");queue.take();}} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();}
}

PriorityBlockingQueue

package Example2131;import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;public class javaDemo {public static void main(String[] args) {BlockingQueue<Integer> queue = new PriorityBlockingQueue<>();Random random = new Random();new Thread(()->{try {for (int i=0;i<5;i++){queue.put(random.nextInt(10));}} catch (InterruptedException e) {throw new RuntimeException(e);}}).start();new Thread(()->{try {for (int i=0;i<5;i++){System.out.println("取出数据"+queue.take());}} catch (InterruptedException e) {throw new RuntimeException(e);}}).start();}
}

PriorityBlockingQueue的特点是:

  • 元素按照优先级进行排序。在示例中,较小的数字具有较高的优先级。
  • 插入和移除操作的时间复杂度为O(logN),其中N为队列中的元素个数。

SynchronousQueue

package Example2132;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args){
//        创建对象和资源量BlockingQueue<String> queue  = new SynchronousQueue<>();
// 创建一个包含各种美食的String数组String[] foods = {"披萨","汉堡", "寿司", "墨西哥炸玉米卷", "牛排", "意大利面", "烤鸭", "富士山寿司", "印度咖喱", "巴西烤肉",};int id;
//        两个厨师new Thread(()->{for (int j=0;j<10;j++){try {
//                        模拟做菜时间TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+"已经做完菜肴"+foods[j]+"并端上座子");queue.put(foods[j]);}catch (Exception e){e.printStackTrace();}}},"厨师").start();for (int i=0;i<10;i++){id = i;new Thread(()->{try {
//                        模拟客人吃饭的时间TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+"享用完"+queue.take()+"这道菜");}catch (Exception e){e.printStackTrace();}},"客人"+i).start();}}
}

SynchronousQueue的特点是:

  • 队列没有容量,每次插入操作必须等待对应的删除操作,反之亦然。
  • 插入和删除操作是成对的,即一个元素的插入必须等待其被消费取出。

 实现子类之间的区别:

  1. ArrayBlockingQueue(数组结构阻塞队列):

    • 基于数组实现的有界队列,具有固定容量。
    • 具有公平(FIFO)和非公平(默认)两种策略的可选择性。
    • 内部使用单个锁来实现线程安全。
    • 插入和移除元素的时间复杂度为O(1)。
  2. LinkedBlockingQueue(链表单端阻塞队列):

    • 基于链表实现的可选有界或无界队列。
    • 默认情况下是无界的,但可以指定最大容量来创建有界队列。
    • 内部使用两个锁来实现线程安全,一个用于插入操作,一个用于移除操作。
    • 插入和移除元素的时间复杂度为O(1)。
  3. PriorityBlockingQueue (优先级阻塞队列):

    • 基于堆实现的无界优先级队列。
    • 元素按照优先级进行排序,优先级通过元素的自然顺序或者自定义比较器进行确定。
    • 内部不允许存储null元素。
    • 插入和移除元素的时间复杂度为O(logN),其中N为队列中的元素个数。
  4. SynchronousQueue(同步队列):

    • 一个没有缓冲区的阻塞队列,用于线程之间直接传输元素。
    • 每个插入操作必须等待相应的移除操作,反之亦然。
    • 队列本身不存储元素,仅用于线程之间的数据传递。
    • 插入和移除操作通常具有较高的可伸缩性性能。

二.双端阻塞队列(BlockingDeque)

BlockingDeque ,可以实现FIFO与FILO操作

什么是FILO?

  1. FILO(First-In, Last-Out)是一种数据处理方式,也被称为后进先出模式。在FILO模式中,最后进入的数据会首先被处理,而最先进入的数据会最后被处理。
  2. 可以将FILO模式理解为堆叠物品的情景,比如在一个书架上放置书籍。当我们将一本新书放在书架上时,它会被放在已有书籍的顶部,因此最后放置的书会处于最上方。当我们需要取出一本书时,会优先从顶部取出最后放置的那本书。这符合FILO模式的处理顺序。
  3. 在计算机科学中,FILO模式常用于栈(Stack)数据结构的操作。栈是一种具有特定数据插入和删除规则的数据结构,最后插入的数据会成为栈顶,最先插入的数据会成为栈底。当需要访问或移除数据时,我们通常会先操作栈顶的数据。
  4. 总之,FILO模式即后进先出模式,用于保持数据处理顺序的一种方式。类似于堆叠物品或栈数据结构,最后进入的数据会首先被处理,而最先进入的数据会最后被处理。

BlockingDeque 的常用方法:

方法描述
addFirst(item)将指定的项添加到双端队列的开头,如果队列已满则抛出异常
addLast(item)将指定的项添加到双端队列的末尾,如果队列已满则抛出异常
offerFirst(item)尝试将指定的项添加到双端队列的开头,如果队列已满则立即返回false,否则返回true
offerLast(item)尝试将指定的项添加到双端队列的末尾,如果队列已满则立即返回false,否则返回true
putFirst(item)将指定的项放入双端队列的开头,如果队列已满则阻塞,直到有空间可用
putLast(item)将指定的项放入双端队列的末尾,如果队列已满则阻塞,直到有空间可用
pollFirst(timeout)从双端队列的开头获取并移除一个项,在指定的超时时间内如果队列为空则返回null
pollLast(timeout)从双端队列的末尾获取并移除一个项,在指定的超时时间内如果队列为空则返回null
takeFirst()从双端队列的开头获取并移除一个项,如果队列为空则阻塞,直到有项可取
takeLast()从双端队列的末尾获取并移除一个项,如果队列为空则阻塞,直到有项可取
getFirst()返回双端队列的开头项,但不对队列进行修改,如果队列为空则抛出异常
getLast()返回双端队列的末尾项,但不对队列进行修改,如果队列为空则抛出异常
peekFirst()返回双端队列的开头项,但不对队列进行修改,如果队列为空则返回null
peekLast()返回双端队列的末尾项,但不对队列进行修改,如果队列为空则返回null
size()返回双端队列中当前的项数
isEmpty()检查双端队列是否为空
clear()清空双端队列,移除所有的项

双端阻塞队列只有一个实现的子类LinkedBlockingDeque

案例代码:

package Example2133;import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {BlockingDeque<Integer> deque = new LinkedBlockingDeque<>();new Thread(()->{for (int i=0;i<10;i++){try {deque.putFirst(i);} catch (InterruptedException e) {throw new RuntimeException(e);}}}).start();new Thread(()->{while (true){try {TimeUnit.SECONDS.sleep(1);System.out.println(deque.takeLast());} catch (InterruptedException e) {throw new RuntimeException(e);}if (deque.isEmpty()){System.out.println("队列空了啦");break;}}}).start();}
}

可以看到双端情况下可以将数据放在头或者尾,获取也可以获取头和尾


三.延迟队列(DelayQueue)

在JUC中提供自动弹出数据延迟的队列DelayQueue,该类属于BlockingQueue的实现子类。如果是创建类对象插入到延迟队列中的话,类需要继承Delayed,并且覆写 compareTo()和getDelay()方法

原理:

  1. 延迟时间计算:每个元素实现了 Delayed 接口,该接口定义了一个 getDelay(TimeUnit unit) 方法,用于计算当前元素距离延迟时间还有多长时间。这个方法返回一个 long 类型的时间值,表示时间单位内的延迟时间。

  2. 队列存储:内部使用有序优先队列(PriorityQueue)来存储元素。元素将根据它们的延迟时间进行排序,即最小的延迟时间的元素将排在队头。

  3. 元素添加:调用 offer(E e) 方法将一个元素添加到队列中。插入元素时,根据其延迟时间,决定其位置。

  4. 元素获取:调用 take() 方法从队列中取出延迟时间到达的元素。如果队列为空,则线程阻塞等待,直到有元素可以取出。

  5. 添加与移除的同步:对队列的添加和移除操作进行同步,以确保多线程环境下的安全性。

  6. 定时删除:元素在队列中的保存时间一旦超过其延迟时间,将会被自动删除。

常用方法:

方法名描述
enqueue(item, delay)将指定的 item 入队,并在 delay 毫秒后执行。
dequeue()出队并返回最早的延迟任务。
getDelay(item)返回指定 item 的剩余延迟时间(以毫秒为单位),如果 item 已经过期则返回负数。
remove(item)从队列中移除指定的 item
size()返回队列中延迟任务的数量。
isEmpty()判断队列是否为空。
clear()清空队列,移除所有的延迟任务。
getExpiredItems(now)返回所有已过期的任务,并从队列中移除它们。
getNextExpiringItem()返回下一个即将过期的任务,但不从队列中移除它。

 案例代码:

package Example2134;import org.jetbrains.annotations.NotNull;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;class Student implements Delayed {private String name;
//    设置停留时间private  long delay;
//    设置离开时间private long expire;Student(String name, long delay , TimeUnit unit){this.name=name;this.delay = TimeUnit.MILLISECONDS.convert(delay,unit);this.expire = System.currentTimeMillis()+this.delay;}@Overridepublic String toString() {return this.name+"同学已经到达预计停留的时间"+TimeUnit.SECONDS.convert(this.delay,TimeUnit.MILLISECONDS)+"秒,已经离开了";}//    延迟时间计算@Overridepublic long getDelay(@NotNull TimeUnit unit) {return unit.convert(this.expire - System.currentTimeMillis(),TimeUnit.MILLISECONDS);}//    队列弹出计算@Overridepublic int compareTo(@NotNull Delayed o) {return (int) (this.delay-this.getDelay(TimeUnit.MILLISECONDS));}
}public class javaDemo {public static void main(String[] args) throws InterruptedException {BlockingQueue<Student> students = new DelayQueue<Student>();students.put(new Student("黄小龙",3,TimeUnit.SECONDS));students.put(new Student("张三",1,TimeUnit.SECONDS));students.put(new Student("李四",5,TimeUnit.SECONDS));while (!students.isEmpty()){Student stu = students.take();System.out.println(stu);TimeUnit.SECONDS.sleep(1);}}
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/100847.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python批量爬虫下载文件——把Excel中的超链接快速变成网址

本文的背景是&#xff1a;大学关系很好的老师问我能不能把Excel中1000个超链接网址对应的pdf文档下载下来。虽然可以手动一个一个点击下载&#xff0c;但是这样太费人力和时间了。我想起了之前的爬虫经验&#xff0c;给老师分析了一下可行性&#xff0c;就动手实践了。    没…

4.SpringCloud 基本架构

1.SpringCloud概述 Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具&#xff08;例如配置管理&#xff0c;服务发现&#xff0c;断路器&#xff0c;智能路由&#xff0c;微代理&#xff0c;控制总线&#xff0c;一次性令牌&#xff0c;全局锁&#xff0c;…

测试框架pytest教程(2)-用例依赖库-pytest-dependency

对于 pytest 的用例依赖管理&#xff0c;可以使用 pytest-dependency 插件。该插件提供了更多的依赖管理功能&#xff0c;使你能够更灵活地定义和控制测试用例之间的依赖关系。 Using pytest-dependency — pytest-dependency 0.5.1 documentation 安装 pytest-dependency 插…

【源码篇】ThreadLocal源码解析(主打的就是通俗易懂,言简意赅)

文章目录 ThreadLocal学习笔记前言1、TheadLocal基本介绍2、ThreadLocal基本使用3、体验ThreadLocal的优点3.1 ThreadLocal与synchronized的比较3.2、ThreadLoca的应用场景 4、ThreadLocal的内部原理4.1 ThreadLocal内部结构4.2 ThreadLocal常用方法分析4.2.1 set原理分析4.2.2…

云计算在IT领域的发展和应用

文章目录 云计算的发展历程云计算的核心概念云计算在IT领域的应用1. 基础设施即服务&#xff08;IaaS&#xff09;&#xff1a;2. 平台即服务&#xff08;PaaS&#xff09;&#xff1a;3. 软件即服务&#xff08;SaaS&#xff09;&#xff1a; 云计算的拓展应用结论 &#x1f3…

学习maven工具

文章目录 &#x1f412;个人主页&#x1f3c5;JavaEE系列专栏&#x1f4d6;前言&#xff1a;&#x1f3e8;maven工具产生的背景&#x1f993;maven简介&#x1fa80;pom.xml文件(project object Model 项目对象模型) &#x1fa82;maven工具安装步骤两个前提&#xff1a;下载 m…

合宙Air724UG LuatOS-Air LVGL API--简介

为何是 LVGL LVGL 是一个开源的图形库&#xff0c;它提供了创建嵌入式 GUI 所需的一切&#xff0c;具有易于使用的图形元素、漂亮的视觉效果和低内存占用的特点。 LVGL特点&#xff1a; 强大的 控件 &#xff1a;按钮、图表、列表、滑动条、图像等 高级图形引擎&#xff1a;动…

[Docker] Portainer + nginx + AList 打造Docker操作三板斧

Portainer : Docker容器图形化管理系统 nginx: 反向代理利器 AList: 文件管理系统 目的: 依托Portainer 的图形管理界面,可视化的配置docker容器. AList再关联Docker各容器内部的配置文件,可视化配置,再配合Portainer重启,日志查看,命令行操作等.对于中小企业对容器化操作简…

ECharts配合Node.js爬虫实现数据可视化

数据可视化简介 可视化技术是将数据和信息以图形化的方式展示出来&#xff0c;以便更好地理解和分析。可视化技术通常使用各种图表、图形、动画和交互式效果来呈现数据。可视化技术有以下几个基本概念&#xff1a; 数据&#xff1a;可视化技术的基础是数据。数据可以是数字、文…

探索软件项目管理的本质及概念

什么是软件项目管理&#xff1f; 软件项目管理是指对软件项目从规划、组织、指挥、控制到最终交付的全过程进行有效管理的一种方法。它通过合理的资源分配、有效的沟通和高效的协作&#xff0c;确保软件项目能够按照预定的目标、时间和质量要求完成。在现代信息技术逐渐普及和…

rabbitmq的发布确认

生产者将信道设置成 confirm 模式&#xff0c;一旦信道进入 confirm 模式&#xff0c; 所有在该信道上面发布的 消息都将会被指派一个唯一的 ID (从 1 开始)&#xff0c;一旦消息被投递到所有匹配的队列之后&#xff0c;broker 就会发送一个确认给生产者(包含消息的唯一 ID)&…

科技云报道:云计算下半场,公有云市场生变,私有云风景独好

科技云报道原创。 大数据、云计算、人工智能&#xff0c;组成了恢弘的万亿级科技市场。这三个领域&#xff0c;无论远观近观&#xff0c;都如此性感和魅力&#xff0c;让一代又一代创业者为之杀伐攻略。 然而高手过招往往一瞬之间便已胜负知晓&#xff0c;云计算市场的巨幕甫…

QTreeWidget——信号处理

文章目录 基本属性信号一、信号种类二、信号测试1、currentItemChanged、itemCollapsed、itemExpanded三个信号的测试2、itemActivated信号3、 itemChanged信号4、其余信号的测试代码&#xff08;包含以上代码&#xff09; 基本属性 信号 一、信号种类 //当前项发生变化时触…

sdk manager (ubuntu20.4) 安装

1、首先下载sdk manager 1.9.3 下载链接 https://www.baidu.com/link?urlVXJhUqxxhS3eFK3bOPTzi5LFl6ybeW3JwDY1CwANaPf1gvO3IxQKzY547NIe53x1blJxnAXg7FTRTvs-cnfnVa&wd&eqida22baa7b0004ca980000000664e2d426 当然要登录自己的账号才能成功下载&#xff0c;下载对应…

解决Spring mvc + JDK17@Resource无法使用的情况

问题描述 我在使用jdk17进行Spring mvc开发时发现 Resource用不了了。 原因 因为JDK版本升级的改动&#xff0c;在Jdk9~17环境下&#xff0c;搭建Springboot项目&#xff0c;会出现原有Resource&#xff08;javax.annotation.Resource&#xff09;不存在的问题&#xff0c;导…

安卓主板定制_电磁屏/电容屏安卓平板基于MTK联发科方案定制

定制化行业平板 在各行各业中的地位越来越重要&#xff0c;甚至在行业转型和发展中发挥着不可替代的作用。随着工业化社会的快速发展&#xff0c;工业生产对智控设备要求越来越高&#xff0c;运用的范畴也越来越普遍广泛&#xff0c;工业级平板就是其中一种应用广泛的设备。 新…

数据结构—树表的查找

7.3树表的查找 ​ 当表插入、删除操作频繁时&#xff0c;为维护表的有序表&#xff0c;需要移动表中很多记录。 ​ 改用动态查找表——几种特殊的树 ​ 表结构在查找过程中动态生成 ​ 对于给定值key ​ 若表中存在&#xff0c;则成功返回&#xff1b; ​ 否则&#xff0…

python 画二部图

1. 特色二部图 修改节点与边颜色、大小等 import networkx as nx import matplotlib.pyplot as plt plt.figure(设备-用户关系图, figsize(4, 6)) # 设置画布大小list_fid [1, 2, 3, 4] # 添加设备节点 list_uid ["a", "b", "c"] # 添加用…

live555server环境搭建

live555环境搭建详解&#xff08;ubuntu18.04&#xff09; 1.环境依赖 openssl可选安不安 安装&#xff08;选择好版本&#xff09; sudo apt-get update sudo apt-get install openssl sudo apt-get install libssl-dev使用头文件是否可用时编译测试时记得链接&#xff08…

Python功能制作之简单的3D特效

需要导入的库&#xff1a; pygame: 这是一个游戏开发库&#xff0c;用于创建多媒体应用程序&#xff0c;提供了处理图形、声音和输入的功能。 from pygame.locals import *: 导入pygame库中的常量和函数&#xff0c;用于处理事件和输入。 OpenGL.GL: 这是OpenGL的Python绑定…