Java并发基础:BlockingQueue和BlockingDeque接口的区别?

news2024/9/25 9:39:55

Java并发基础:BlockingQueue和BlockingDeque接口的区别? - 程序员古德

核心概念

BlockingQueueBlockingDeque 它们都支持在并发编程中的线程安全操作,但是,这两个接口之间存在一些关键的区别,主要在于它们所支持的操作和数据结构的特性,如下:

1、数据结构特性

  1. BlockingQueue 是一个支持线程安全的队列,即它遵循 FIFO(先进先出)原则,可以向队列的尾部添加元素,并从队列的头部移除元素。
  2. BlockingDeque 是一个支持线程安全的双端队列(Deque,也称为双头队列),因此,可以在队列的头部和尾部添加或移除元素,BlockingDeque 提供了比 BlockingQueue 更多的操作灵活性。

2、操作

  1. BlockingQueue 提供了基本的队列操作,如 add(), offer(), put(), take(), poll() 等,这些方法主要用于在队列的尾部添加元素和从队列的头部移除元素。
  2. BlockingDeque 除了提供与 BlockingQueue 类似的操作外(但通常是以不同的名称提供,例如 offerFirst(), offerLast(), takeFirst(), takeLast() 等),还支持在队列的头部进行添加和移除操作的方法,如 offerFirst(), pollFirst(), peekFirst() 等,此外,它还提供了 push(), pop() 等栈操作,因为双端队列可以模拟栈的行为。

3、使用场景

  1. 当需要一个简单的、线程安全的 FIFO 队列时,BlockingQueue 是一个很好的选择,它通常用于生产者-消费者场景,其中生产者将数据放入队列,消费者从队列中取出数据。
  2. 当需要更多的灵活性,例如在队列的头部和尾部都能添加或移除元素时,BlockingDeque 是一个更好的选择,这种数据结构在需要同时维护队列和栈行为的场景中特别有用。

4、实现类

  1. BlockingQueue 提供了多种实现,如 ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue 等。
  2. BlockingDeque,常见的实现是 LinkedBlockingDeque,这个实现提供了一个基于链表的双端队列,支持在队列的两端进行高效的插入和移除操作。

代码案例

Java并发基础:BlockingQueue和BlockingDeque接口的区别? - 程序员古德

BlockingQueue

BlockingQueue 接口表示一个可以存取元素,并且线程安全的队列,换句话说,多个线程可以同时从这个队列中安全地插入或者移除元素,BlockingQueue 的特性在于它支持在队列为空时,获取元素的线程将会等待,直到有元素可获取;当队列已满时,试图插入元素的线程将会等待,直到队列中有可用的空间,它的主要功能如下:

  1. 线程安全BlockingQueue 的所有操作都是线程安全的,这意味着在多线程环境中,可以安全地添加或移除元素,而不需要额外的同步措施。
  2. 阻塞操作:当队列为空时,从队列中获取元素的线程将会被阻塞,直到其他线程向队列中插入元素,同样,当队列已满时,试图插入元素的线程也会被阻塞,直到队列中有空间可用。
  3. 支持限时等待:除了无限期的等待外,BlockingQueue 还提供了带有超时参数的方法,允许线程在指定的时间内等待元素的插入或移除。
  4. 容量可选BlockingQueue 的实现类可以选择有界或无界,有界队列有一个固定的容量,而无界队列的容量则只受限于可用内存。

它有以下使用场景:

  1. 生产者-消费者模式:这是 BlockingQueue 最常见的使用场景,在这种模式中,生产者线程生成数据并将其放入队列,而消费者线程从队列中取出数据并处理,BlockingQueue 简化了生产者-消费者模式的实现,因为它负责处理线程间的同步和通信。
  2. 任务调度BlockingQueue 也可以用于任务调度系统中,在这种情况下,可以将待处理的任务作为元素添加到队列中,然后由工作线程从队列中取出任务并处理。
  3. 缓冲:在需要缓冲数据流或事件流的系统中,BlockingQueue 可以作为缓冲区使用,它允许数据或事件的生产者和消费者以不同的速率运行,而不会丢失数据或造成拥塞。
  4. 线程池:在 ExecutorService 框架中,BlockingQueue 用于存储待执行的任务,线程池中的工作线程从队列中取出任务并执行。

下面是一个简单的生产者-消费者示例,展示了如何使用 BlockingQueue,如下代码:

import java.util.concurrent.BlockingQueue;  
import java.util.concurrent.LinkedBlockingQueue;  
  
public class ProducerConsumerExample {  
      
    public static void main(String[] args) {  
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);  
          
        // 生产者线程  
        Thread producer = new Thread(() -> {  
            for (int i = 0; i < 10; i++) {  
                try {  
                    System.out.println("生产者生产了 " + i);  
                    queue.put(i); // 如果队列满了,将会阻塞  
                    Thread.sleep(200); // 模拟生产时间  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        });  
          
        // 消费者线程  
        Thread consumer = new Thread(() -> {  
            for (int i = 0; i < 10; i++) {  
                try {  
                    Integer item = queue.take(); // 如果队列为空,将会阻塞  
                    System.out.println("消费者消费了 " + item);  
                    Thread.sleep(500); // 模拟消费时间  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        });  
          
        // 启动线程  
        producer.start();  
        consumer.start();  
    }  
}

BlockingDeque

BlockingDeque结合了BlockingQueue的阻塞特性和Deque(双端队列)的双端操作能力,从而提供了一个线程安全的、支持在两端添加和移除元素的阻塞队列,它有以下主要功能:

  1. 线程安全BlockingDeque的所有操作都是线程安全的,因此多个线程可以安全地同时访问它。
  2. 阻塞操作:如果队列为空,尝试从队列中取出元素的线程将会被阻塞,直到其他线程向队列中插入元素,同样,如果队列已满(对于有界队列),尝试插入元素的线程也会被阻塞,直到队列中有空间可用。
  3. 双端操作:与普通的Deque一样,BlockingDeque支持在队列的两端添加(addFirst, addLast, offerFirst, offerLast)和移除元素(removeFirst, removeLast, pollFirst, pollLast),此外,它还提供了检查队列两端元素的方法(getFirst, getLast, peekFirst, peekLast)。
  4. 容量可选BlockingDeque的实现类可以选择有界或无界,有界队列有一个固定的容量,而无界队列的容量则只受限于可用内存。

它有以下使用场景:

  1. 生产者-消费者模式:这是BlockingDeque最常见的使用场景之一,生产者线程在队列的一端添加元素,而消费者线程在另一端移除元素,这种模式特别适用于需要缓冲数据流或任务队列的系统。
  2. 工作窃取算法:在并行计算中,BlockingDeque可以用作工作窃取队列,实现工作线程之间的任务分配,当一个线程完成了自己的任务时,它可以从其他线程的任务队列中“窃取”任务来执行。
  3. 双端操作的需求:任何需要在队列的两端进行添加和移除操作的并发场景都可以使用BlockingDeque,例如,实现一个并发的LRU(最近最少使用)缓存时,可能需要使用BlockingDeque来维护访问顺序。

下面是一个使用BlockingDeque的简单生产者-消费者示例,如下代码:

import java.util.concurrent.BlockingDeque;  
import java.util.concurrent.LinkedBlockingDeque;  
  
public class ProducerConsumerWithBlockingDeque {  
  
    public static void main(String[] args) {  
        BlockingDeque<Integer> deque = new LinkedBlockingDeque<>(5);  
  
        // 生产者线程  
        Thread producer = new Thread(() -> {  
            for (int i = 0; i < 10; i++) {  
                try {  
                    System.out.println("生产者生产了 " + i);  
                    deque.putFirst(i); // 在队列头部插入元素,如果队列满了,将会阻塞  
                    Thread.sleep(200); // 模拟生产时间  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        });  
  
        // 消费者线程  
        Thread consumer = new Thread(() -> {  
            for (int i = 0; i < 10; i++) {  
                try {  
                    Integer item = deque.takeLast(); // 从队列尾部移除元素,如果队列为空,将会阻塞  
                    System.out.println("消费者消费了 " + item);  
                    Thread.sleep(500); // 模拟消费时间  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        });  
  
        // 启动线程  
        producer.start();  
        consumer.start();  
    }  
}

在这个示例中,创建了一个容量为5的LinkedBlockingDeque,生产者线程在队列的头部插入整数,而消费者线程在队列的尾部移除整数,注意,当队列满时,生产者线程会被阻塞;同样,当队列空时,消费者线程也会被阻塞,此外,使用putFirsttakeLast方法来演示双端队列的特性,也可以使用putLasttakeFirst来改变插入和移除元素的顺序。

关注我,每天学习互联网编程技术 - 程序员古德

END!

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

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

相关文章

单选全选功能实现

单选框&#xff1a; // v-for"i in carStore.cartList" i 是购物车里的单类商品 <el-checkbox :model-value"i.selected" change"(selected)>singeCheck(i,selected)"/>全选框&#xff1a; <el-checkbox :model-value"carSto…

Sublime Text 3配置 Node.js 开发环境

《开发工具系列》 Sublime Text 3配置 Node.js 开发环境 一、引言二、主要内容2.1 初识 Sublime Text 32.2 初识 Node.js2.3 接入 Node.js2.3.1 下载并安装 Node.js2.3.2 环境变量配置 2.4 配置 Node.js 开发环境2.5 编写 Node.js 代码2.6 运行 Node.js 代码 三、总结 一、引言…

vue3:25—其他API

目录 1、shallowRef和shallowReactive 2、readonly与shallowReadonly readonly shallowReadonly 3、toRaw和markRaw toRaw markRaw 4、customRef 1、shallowRef和shallowReactive shallowRef 1.作用:创建一个响应式数据&#xff0c;但只对顶层属性进行响应式处理。2…

Java基于微信小程序的驾校报名小程序,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Pytorch+NCCL源码编译

目录 环境1. 安装cudnn2. 使用pytorch自带NCCL库进行编译3. 修改NCCL源代码并重新编译后测试&#xff0c;体现出源码更改 环境 Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-91-generic x86_64)cuda 11.8 cudnn 8python 3.10torch V2.0.1 nccl 2.14.3NVIDIA GeForce RTX 4090 *2 1.…

汽车控制臂的拓扑优化

前言 本示例使用优化模块通过减小控制臂的体积同时最大化其刚度来优化汽车控制臂的设计。 本页讨论 前言应用描述Abaqus建模方法和仿真技术文件参考 应用描述 本例说明了汽车控制臂的拓扑优化&#xff0c;在拓扑优化过程中&#xff0c;修改设计区域中单元的材料特性(有效地从…

我的PyTorch模型比内存还大,怎么训练呀?

原文&#xff1a;我的PyTorch模型比内存还大&#xff0c;怎么训练呀&#xff1f; - 知乎 看了一篇比较老&#xff08;21年4月文章&#xff09;的不大可能训练优化方案&#xff0c;保存起来以后研究一下。 随着深度学习的飞速发展&#xff0c;模型越来越臃肿&#xff0c;哦不&a…

2. Maven 继承与聚合

目录 2. 2.1 继承 2.2继承关系 2.2.1 思路分析 2.2.2 实现 2.1.2 版本锁定 2.1.2.1 场景 2.1.2.2 介绍 2.1.2.3 实现 2.1.2.4 属性配置 2.2 聚合 2.2.1 介绍 2.2.2 实现 2.3 继承与聚合对比 maven1&#xff1a;分模块设计开发 2. 在项目分模块开发之后啊&#x…

io和File的综合练习:

先来说说字节流和字符流的应用场景 练习一&#xff1a; /*拷贝一个文件夹考虑子文件夹*///源文件夹路径File src new File("E:\\aaa-FIle学习测试\\bbb");//目的文件夹路径File dest new File("E:\\aaa-FIle学习测试\\ccc");copy(src,dest);}public stati…

next项目页面性能调优

next项目页面性能调优 一般来说性能优化可以分为加载时、运行时两部分的优化。 扩展参考链接&#xff1a; 前端性能优化 24 条建议 Webpack 4进阶–从前的日色变得慢 &#xff0c;一下午只够打一次包 Webpack 分包优化首屏加载 参考指标 FCP&#xff08;First Contentful P…

《MySQL 简易速速上手小册》第3章:性能优化策略(2024 最新版)

文章目录 3.1 查询优化技巧3.1.1 基础知识3.1.2 重点案例3.1.3 拓展案例 3.2 索引和查询性能3.2.1 基础知识3.2.2 重点案例3.2.3 拓展案例 3.3 优化数据库结构和存储引擎3.3.1 基础知识3.3.2 重点案例3.3.3 拓展案例 3.1 查询优化技巧 让我们来聊聊如何让你的 MySQL 查询跑得像…

【Linux】vim的基本操作与配置(上)

Hello everybody!今天我们要进入vim的讲解了。学会了vim,咱们就可以在Linux系统上做一些简单的编程啦&#xff01; 那么废话不多说&#xff0c;咱们直接进入正题&#xff01; 1.初识vim vim是一款多模式的文本编辑器&#xff0c;可以对一个文件进行编辑操作。 它一共有三个模…

【射影几何13 】梅氏定理和塞瓦定理探讨

梅氏定理和塞瓦定理 目录 一、说明二、梅涅劳斯&#xff08;Menelaus&#xff09;定理三、塞瓦(Giovanni Ceva&#xff09;定理四、塞瓦点的推广 一、说明 在射影几何中&#xff0c;梅涅劳斯&#xff08;Menelaus&#xff09;定理和塞瓦定理是非常重要的基本定理。通过这两个定…

09 AB 10串口通信发送原理

通用异步收发传输器&#xff08; Universal Asynchronous Receiver/Transmitter&#xff0c; UART&#xff09;是一种异步收发传输器&#xff0c;其在数据发送时将并行数据转换成串行数据来传输&#xff0c; 在数据接收时将接收到的串行数据转换成并行数据&#xff0c; 可以实现…

【数据分享】1929-2023年全球站点的逐年平均降水量(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;说到常用的降水数据&#xff0c;最详细的降水数据是具体到气象监测站点的降水数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2023年全…

训练集,验证集,测试集比例

三者的区别 训练集&#xff08;train set&#xff09; —— 用于模型拟合的数据样本。验证集&#xff08;validation set&#xff09;—— 是模型训练过程中单独留出的样本集&#xff0c;它可以用于调整模型的超参数和用于对模型的能力进行初步评估。 通常用来在模型迭代训练时…

DevOps落地笔记-17|度量指标:寻找真正的好指标?

前面几个课时端到端地介绍了软件开发全生命周期中涉及的最佳实践&#xff0c;经过上面几个步骤&#xff0c;企业在进行 DevOps 转型时技术方面的问题解决了&#xff0c;这个时候我们还缺些什么呢&#xff1f;事实上很多团队和组织在实施 DevOps 时都专注于技术&#xff0c;而忽…

【力扣】查找总价格为目标值的两个商品,双指针法

查找总价格为目标值的两个商品原题地址 方法一&#xff1a;双指针 这道题和力扣第一题“两数之和”非常像&#xff0c;区别是这道题已经把数组排好序了&#xff0c;所以不考虑暴力枚举和哈希集合的方法&#xff0c;而是利用单调性&#xff0c;使用双指针求解。 考虑数组pric…

零代码3D可视化快速开发平台

老子云平台 老子云3D可视化快速开发平台&#xff0c;集云压缩、云烘焙、云存储云展示于一体&#xff0c;使3D模型资源自动输出至移动端PC端、Web端&#xff0c;能在多设备、全平台进行展示和交互&#xff0c;是全球领先、自主可控的自动化3D云引擎。此技术已经在全球申请了专利…