阻塞队列:原理、应用及实现

阻塞队列:原理、应用及实现

    • 什么是阻塞队列
    • 以生产消费者模型形象地理解阻塞队列
    • 阻塞队列实现生产消费者模型
    • 模拟实现阻塞队列实现生产消费者模型

什么是阻塞队列

阻塞队列是一种特殊且实用的队列数据结构,它同样遵循 “先进先出” 的原则。与普通队列不同的是,阻塞队列是线程安全的,这使得它在多线程编程中扮演着重要角色。

阻塞队列具有以下两个关键特性:

  • 当队列已满时,如果有线程尝试将元素入队列,该线程将会被阻塞,直到有其他线程从队列中取走元素,使得队列腾出空间。
  • 当队列为空时,若有线程试图从队列中出队列,此线程也会被阻塞,直至有其他线程向队列中插入新的元素。

阻塞队列的一个经典应用场景便是 “生产者消费者模型”。这种模型在软件开发中被广泛使用,能够有效地协调不同线程之间的工作,提高程序的性能和稳定性。

以生产消费者模型形象地理解阻塞队列

为了更直观地理解阻塞队列的工作原理,我们以一个具体的生产消费者场景为例。假设有四个生产者线程和一个消费者线程:

  • 每个生产者线程每单位时间能够生产 4 个面包。
  • 消费者线程每单位时间只能取走 1 个面包。
    在这里插入图片描述

在这种情况下,由于生产者的生产速度远快于消费者的消费速度,每单位时间会有 3 个面包进入阻塞队列,等待消费者线程来取。并且在这个过程中,生产者线程不会停止生产。
在这里插入图片描述

随着时间的推移,当阻塞队列被填满,没有空位时,生产者线程就会因为无法继续入队列而停止生产,直到消费者线程从队列中取走一些面包,腾出空间。
在这里插入图片描述

阻塞队列实现生产消费者模型

在 Java 中,BlockingQueue接口及其实现类为我们提供了便捷的方式来实现生产消费者模型。以下是 BlockingQueue 的构造方法示例图:

在这里插入图片描述

下面通过一段 Java 代码来具体实现生产消费者模型(为了便于观察和理解,我们创建了一个固定容量的阻塞队列):

import java.util.Random;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;public class Main {public static void main(String[] args) {Random random = new Random();// 首先创建一个阻塞队列,容量为 10BlockingDeque<Integer> blockingDeque = new LinkedBlockingDeque<>(10);// 创建生产者线程Thread t1 = new Thread(() -> {while (true) {try {int value = random.nextInt(100);System.out.println("生产元素:" + value);blockingDeque.put(value);} catch (InterruptedException e) {throw new RuntimeException(e);}}});// 创建消费者线程Thread t2 = new Thread(() -> {while (true) {try {System.out.println("消费元素:" + blockingDeque.take());Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

通过上述代码,生产者线程不断生产随机整数并放入阻塞队列,而消费者线程则从队列中取出元素并打印,同时每隔 1 秒消费一次,模拟实际的消费速度。
在这里插入图片描述

模拟实现阻塞队列实现生产消费者模型

除了使用 Java 提供的现成阻塞队列实现类,我们还可以自己模拟实现一个固定容量的阻塞队列。在这个实现过程中,需要重点关注以下三个方面:

  1. 确保线程安全,避免多个线程同时访问队列时出现数据不一致的问题。
  2. 当队列为空时,消费者线程需要阻塞等待,直到队列中有新的元素可供消费。
  3. 当队列为满时,生产者线程需要阻塞等待,直到队列中有元素被消费,腾出空间。

以下是模拟实现的代码:

public class MyBlockqueue {// 队列的最大容量int max = 10;// 创建一个固定容量的数组来存储队列元素int[] arr;// 记录队列中已有元素的个数int num;// 队列头元素的下标,遵循先进先出原则int first;// 队列尾元素的下标,遵循后进后出原则int end;public MyBlockqueue(int max) {this.max = max;arr = new int[max];}// 模拟实现入队列操作public void put(int x) throws InterruptedException {// 检查队列是否已满while (num >= max) {// 如果已满,当前线程进入阻塞状态synchronized (this) {wait();}}// 队列未满,添加元素synchronized (this) {arr[end] = x;end = (end + 1) % max;num++;// 唤醒其他可能在等待的线程notify();}}// 模拟实现出队列操作public Integer take() throws InterruptedException {// 检查队列是否为空while (num == 0) {// 如果为空,当前线程进入阻塞状态synchronized (this) {wait();}}// 队列不为空,取出元素synchronized (this) {int tmp = arr[first];first = (first + 1) % max;num--;// 唤醒其他可能在等待的线程notify();return tmp;}}
}

接下来,我们使用这个自定义的阻塞队列来实现生产消费者模型:

import java.util.Random;public class Main {public static void main(String[] args) {Random random = new Random();// 创建一个自定义的阻塞队列,容量为 10MyBlockqueue blockingDeque = new MyBlockqueue(10);// 创建生产者线程Thread t1 = new Thread(() -> {while (true) {try {int value = random.nextInt(100);System.out.println("生产元素:" + value);blockingDeque.put(value);} catch (InterruptedException e) {throw new RuntimeException(e);}}});// 创建消费者线程Thread t2 = new Thread(() -> {while (true) {try {System.out.println("消费元素:" + blockingDeque.take());Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

在这里插入图片描述

通过上述代码,我们成功地模拟实现了一个阻塞队列,并基于它构建了生产消费者模型,帮助我们更深入地理解阻塞队列的工作原理和应用场景。

希望这篇博客能让你对阻塞队列有更全面、深入的认识!

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

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

相关文章

【开源宝藏】30天学会CSS - DAY5 第五课 脉冲动画

以下是一个完整的渐进式教程&#xff0c;拆解如何用 HTML CSS 构建“Pulsar”水波脉冲动画。通过阅读&#xff0c;你将理解每个核心属性与关键帧如何配合&#xff0c;让一个小圆不断散发动态波纹&#xff0c;并且文字始终停留在圆心。 第 0 步&#xff1a;项目概览 文件结构示…

2060 裁纸刀

2060 裁纸刀 ⭐️难度&#xff1a;简单 &#x1f31f;考点&#xff1a;2022、规律、思维 &#x1f4d6; &#x1f4da; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {static int N 100010…

TextView、AppCompatTextView和MaterialTextView该用哪一个?Android UI 组件发展史与演进对照表

在 Android 开发中&#xff0c;UI 组件一直在不断演进&#xff0c;从最初的原生组件&#xff0c;到 Support Library&#xff08;AppCompat 兼容库&#xff09;&#xff0c;再到如今的 Material Design 组件。这篇文章将梳理 Android UI 组件的发展历史&#xff0c;并提供详细的…

python学习笔记--实现简单的爬虫(一)

任务&#xff1a;爬取豆瓣最受欢迎的250个电影的资料 链接&#xff1a;豆瓣电影 Top 250 用浏览器打开后&#xff0c;使用F12或鼠标右键--检查&#xff0c;查看网页的源代码&#xff0c;分析网页结构&#xff0c;如下图所示&#xff1a; 分析后得知&#xff1a; 1.电影名位于…

Postgresql 删除数据库报错

1、删除数据库时&#xff0c;报错存在其他会话连接 ## 错误现象&#xff0c;存在其他的会话连接正在使用数据库 ERROR: database "cs" is being accessed by other users DETAIL: There is 1 other session using the database.2、解决方法 ## 终止被删除数据库下…

self Attention为何除以根号dk?(全新角度)

全网最独特解析&#xff1a;self Attention为何除根号dk&#xff1f; 一、假设条件&#xff1a;查询向量和键向量服从正态分布 假设查询向量 q i q_i qi​和键向量 k j k_j kj​的每个分量均为独立同分布的随机变量&#xff0c;且服从标准正态分布&#xff0c;即&#xff1a;…

numpy学习笔记10:arr *= 2向量化操作性能优化

numpy学习笔记10&#xff1a;arr * 2向量化操作性能优化 在 NumPy 中&#xff0c;直接对整个数组进行向量化操作&#xff08;如 arr * 2&#xff09;的效率远高于显式循环&#xff08;如 for i in range(len(arr)): arr[i] * 2&#xff09;。以下是详细的解释&#xff1a; 1. …

Cursor+Claude-3.5生成Android app

一、Android Studio下载 https://developer.android.com/studio?hlzh-tw#get-android-studio 等待安装完成 二、新建工程 点击new project 选择Empty Activity 起一个工程名 当弹出这个框时 可以在settings里面选择No proxy 新建好后如下 点击右边模拟器&#xff0c…

WPF Reactive 数据绑定

文章目录 Combox 绑定List-通过枚举绑定方法一:方法二:Button 绑定TextBlock绑定NumericUpDown绑定Expander绑定checkbox绑定NumericUpDownCombox 绑定List-通过枚举绑定 方法一: ViewControl using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; usin…

算法及数据结构系列 - 滑动窗口

系列文章目录 算法及数据结构系列 - 二分查找 算法及数据结构系列 - BFS算法 算法及数据结构系列 - 动态规划 算法及数据结构系列 - 双指针 算法及数据结构系列 - 回溯算法 算法及数据结构系列 - 树 文章目录 滑动窗口框架思路经典题型76. 最小覆盖子串567. 字符串的排列438. …

Android adb调试应用程序

启动app 有的时候app不是预先安装的&#xff0c;也不能从界面start一个app&#xff0c;这时需要后台拉起app。 $adb shell am start package.name/Activity.name 例如&#xff0c;android原生camera app&#xff0c; 包名为com.android.camera2&#xff0c; mainActivity名为…

Java EE(15)——网络原理——TCP协议解析一

一.确认应答/(确认)序列号 接收方接收到数据后&#xff0c;向发送方返回一个确认信号(ack)&#xff0c;告诉发送方数据被成功接收。ACK报文段只是作为确认使用的&#xff0c;一般来说不携带应用层数据&#xff08;载荷&#xff09;&#xff0c;也就是说只有报头部分。但有可能…

node-ddk,electron 组件, 打开新窗口

node-ddk 打开新窗口 https://blog.csdn.net/eli960/article/details/146207062 也可以下载demo直接演示 http://linuxmail.cn/go#node-ddk 本文讲解如何在渲染进程发起创建新窗口, 包括 window.open 在主进程定义窗口类型 import main, { NODEDDK } from "node-ddk…

git管理时keil项目忽略文件列表

在使用 Git 管理 Keil MDK&#xff08;μVision 5&#xff09;工程时&#xff0c;需要忽略编译生成的临时文件、调试文件、用户配置等非必要内容。以下是忽略文件的详细列表及说明&#xff0c;可直接保存为 .gitignore 文件&#xff1a; Keil MDK 工程的 .gitignore 文件 giti…

C#单例模式

单例模式 (Singleton),保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。通常我们可以让一个全局变量使得一个对象被访问&#xff0c;但它不能防止你实例化对个对象&#xff0c;一个最好的办法就是&#xff0c;让类自身负责保护它的唯一实例。这个类可以保证没…

ZYNQ的cache原理与一致性操作

在Xilinx Zynq SoC中&#xff0c;Cache管理是确保处理器与外部设备&#xff08;如FPGA逻辑、DMA控制器&#xff09;之间数据一致性的关键。Zynq的ARM Cortex-A9处理器包含L1 Cache&#xff08;指令/数据&#xff09;和L2 Cache&#xff0c;其刷新&#xff08;Flush/Invalidate&…

Linux NFS、自动挂载与系统启动管理指南

1. NFS客户端挂载导出的目录的方式 NFS&#xff08;网络文件系统&#xff09; 允许将远程服务器的目录挂载到本地&#xff0c;像访问本地文件一样操作远程文件。挂载方式主要有两种&#xff1a; 手动挂载&#xff1a;使用 mount 命令&#xff08;临时生效&#xff0c;重启后丢…

NO.55十六届蓝桥杯备战|排序|插入|选择|冒泡|堆|快速|归并(C++)

插⼊排序 插⼊排序(Insertion Sort)类似于玩扑克牌插牌过程&#xff0c;每次将⼀个待排序的元素按照其关键字⼤⼩插⼊到前⾯已排好序的序列中&#xff0c;按照该种⽅式将所有元素全部插⼊完成即可 #include <iostream> using namespace std; const int N 1e5 10; …

【Oracle资源损坏类故障】:详细了解坏块

目录 1、物理坏块与逻辑坏块 1.1、物理坏块 1.2、逻辑坏块 2、两个坏块相关的参数 2.1、db_block_checksum 2.2、db_block_checking 3、检测坏块 3.1、告警日志 3.2、RMAN 3.3、ANALYZE 3.4、数据字典 3.5、DBVERIFY 4、修复坏块 4.1、RMAN修复 4.2、DBMS_REPA…

计算机网络高频(二)TCP/IP基础

计算机网络高频(二)TCP/IP基础 1.什么是TCP/IP⭐⭐ TCP/IP是一种网络通信协议,它是互联网中最常用的协议之一。TCP/IP有两个基本的协议:TCP(传输控制协议)和IP(互联网协议)。 TCP(Transmission Control Protocol,传输控制协议)是一种可靠的、面向连接的协议。它负…