Java并发基础:ConcurrentLinkedDeque全面解析!

news2024/11/29 20:32:46

Java并发基础:ConcurrentLinkedDeque全面解析! - 程序员古德

内容概要

ConcurrentLinkedDeque类提供了线程安全的双端队列操作,支持高效的并发访问,因此在多线程环境下,可以放心地在队列的两端添加或移除元素,而不用担心数据的一致性问题。同时,它的内部实现采用了无锁算法,从而避免了传统锁带来的性能开销。

核心概念

假如,有一个在线聊天应用,该应用允许多个用户同时在线聊天,它们可以创建不同的聊天室,并在这些聊天室里发送和接收消息,为了处理和存储这些消息,因此需要一个数据结构来存储和管理它们。

可以将每个聊天室看作是一个ConcurrentLinkedDeque实例,其中的每个元素都是一条消息,由于ConcurrentLinkedDeque是线程安全的,这意味着多个线程可以同时向同一个聊天室添加或删除消息,而不会导致数据混乱或不一致。

当用户发送一条消息时,可以将这条消息添加到相应聊天室的ConcurrentLinkedDeque的尾部,而当用户查看聊天室的消息历史时,可以从ConcurrentLinkedDeque的头部开始遍历并显示消息,由于ConcurrentLinkedDeque支持在两端进行高效的操作,因此这种使用场景非常合适。ConcurrentLinkedDeque还提供了更加安全的并发操作方法,如offerFirstofferLastpollFirstpollLast等,这些方法可以在多线程环境下安全地添加和删除元素。

ConcurrentLinkedDeque实现了Deque接口,并提供了一个线程安全的双端队列,这个数据结构被设计用来解决多线程环境下的并发问题,特别是在需要线程安全地从队列的两端添加或者移除元素时,它通常适合处理如下类似的场景的问题:

  1. 线程安全:在多线程环境下,普通的集合类(如ArrayList, LinkedList)不是线程安全的,如果多个线程同时修改这些集合,可能会导致数据不一致或者其他未定义的行为,ConcurrentLinkedDeque通过内部的并发控制机制,确保了多个线程可以安全地同时访问和修改队列。
  2. 高并发性能:与SynchronizedDeque相比,ConcurrentLinkedDeque通过无锁(lock-free)或者最小化锁竞争的设计,提供了更高的吞吐量,它在内部使用了一种称为CAS(Compare-and-Swap)的原子操作来实现无锁算法,从而减少了线程间的竞争和阻塞。
  3. 双端操作:与只能在一端添加或移除元素的队列(如BlockingQueue接口的实现类)不同,ConcurrentLinkedDeque支持在队列的两端进行高效的添加和移除操作,这使得它在某些算法和数据结构中特别有用,比如实现一个线程安全的LRU缓存
  4. 内存一致性ConcurrentLinkedDeque保证了内存一致性效应,即一个线程对队列的修改对其他线程是立即可见的**(遵循happens-before关系)**,这是通过内部的volatile变量和适当的同步机制来实现的。

代码案例

以下是一个简单的代码示例,演示了如何使用ConcurrentLinkedDeque类,该示例模拟了一个多线程环境中的生产者-消费者场景,其中生产者线程向队列中添加元素,而消费者线程从队列中移除并处理元素,如下代码:

import java.util.concurrent.ConcurrentLinkedDeque;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.TimeUnit;  
  
public class ConcurrentLinkedDequeExample {  
  
    // 创建一个ConcurrentLinkedDeque实例  
    private static final ConcurrentLinkedDeque<Integer> deque = new ConcurrentLinkedDeque<>();  
  
    // 生产者任务:向队列中添加元素  
    private static class Producer implements Runnable {  
        @Override  
        public void run() {  
            int producedCount = 0;  
            while (producedCount < 10) { // 生产10个元素后停止  
                deque.offerLast(producedCount++); // 在队列尾部添加元素  
                System.out.println("Produced: " + (producedCount - 1));  
                try {  
                    // 稍微延迟一下,模拟生产过程  
                    Thread.sleep(100);  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt();  
                    return;  
                }  
            }  
        }  
    }  
  
    // 消费者任务:从队列中移除并处理元素  
    private static class Consumer implements Runnable {  
        @Override  
        public void run() {  
            while (true) {  
                Integer consumed = deque.pollFirst(); // 尝试从队列头部移除元素  
                if (consumed == null) {  
                    // 队列为空,退出循环(在实际应用中可能需要更复杂的退出条件)  
                    break;  
                }  
                System.out.println("Consumed: " + consumed);  
                try {  
                    // 稍微延迟一下,模拟消费过程  
                    Thread.sleep(150);  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt();  
                    return;  
                }  
            }  
        }  
    }  
  
    public static void main(String[] args) throws InterruptedException {  
        // 创建一个固定大小的线程池  
        ExecutorService executorService = Executors.newFixedThreadPool(3);  
  
        // 提交一个生产者任务到线程池  
        executorService.submit(new Producer());  
  
        // 提交两个消费者任务到线程池  
        executorService.submit(new Consumer());  
        executorService.submit(new Consumer());  
  
        // 让主线程等待一段时间,以便生产者和消费者任务可以执行完成  
        // 注意:在实际应用中,应该使用更可靠的机制来等待任务的完成,比如Future.get()或CountDownLatch等。  
        TimeUnit.SECONDS.sleep(5);  
  
        // 关闭线程池(这会导致正在执行的任务被中断,因此在实际应用中需要谨慎处理)  
        executorService.shutdownNow();  
        // 等待线程池终止(这里是为了示例代码的完整性,实际应用中可能需要根据具体情况来处理)  
        executorService.awaitTermination(1, TimeUnit.MINUTES);  
    }  
}

核心API

下面是一些 ConcurrentLinkedDeque 中常用的方法及其含义:

1、添加元素

  1. offerFirst(E e): 将指定的元素插入此双端队列的开头,如果成功则返回 true,如果当前没有可用空间则返回 false
  2. offerLast(E e): 将指定的元素插入此双端队列的末尾,如果成功则返回 true,如果当前没有可用空间则返回 false
  3. addFirst(E e), addLast(E e): 类似于 offerFirstofferLast,但是如果添加失败会抛出 IllegalStateException
  4. push(E e): 将元素推入此双端队列所表示的堆栈(如果可能的话),等效于 addFirst

2、移除元素

  1. pollFirst(): 获取并移除此双端队列的第一个元素,或返回 null 如果此双端队列为空。
  2. pollLast(): 获取并移除此双端队列的最后一个元素,或返回 null 如果此双端队列为空。
  3. removeFirst(), removeLast(): 类似于 pollFirstpollLast,但是如果双端队列为空会抛出 NoSuchElementException
  4. pop(): 从此双端队列所表示的堆栈中弹出一个元素,等效于 removeFirst

3、检查元素

  1. peekFirst(): 获取但不移除此双端队列的第一个元素,或返回 null 如果此双端队列为空。
  2. peekLast(): 获取但不移除此双端队列的最后一个元素,或返回 null 如果此双端队列为空。

4、其他方法

  1. isEmpty(): 如果此双端队列不包含任何元素,则返回 true
  2. size(): 返回此双端队列中的元素数量。注意,由于并发修改,返回的数量可能只是近似值。
  3. iterator(): 返回在此双端队列的元素上进行迭代的迭代器。返回的迭代器是弱一致性的。
  4. descendingIterator(): 返回在此双端队列的元素上进行逆序迭代的迭代器。返回的迭代器是弱一致性的。

5、并发控制相关

  1. 由于 ConcurrentLinkedDeque 的设计是为了支持高并发,因此它内部的实现使用了复杂的非阻塞算法和原子操作来确保线程安全。
  2. 与此同时,ConcurrentLinkedDeque 的迭代器是弱一致性的,这意味着它们能够反映出它们被构造时原始集合的某个状态,但是如果集合在迭代过程中被并发修改,迭代器不一定能够反映这些修改。

核心总结

Java并发基础:PriorityBlockingQueue全面解析! - 程序员古德

ConcurrentLinkedDeque类是一个高效、线程安全的双端队列,有着出色的并发性能,能够在多线程环境下保持较高的吞吐量,且支持在队列两端进行快速的插入和删除操作,采用无锁算法,使它避免了传统锁机制带来的性能开销和死锁风险。在高并发场景下,由于无锁算法的复杂性,可能会导致较高的CPU占用率,此外,其size()方法返回的元素数量是近似值,不适合需要精确计数的场景

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

END!
END!
END!

往期回顾

精品文章

Java并发基础:PriorityBlockingQueue全面解析!

Java并发基础:DelayQueue全面解析!

Java并发基础:LinkedBlockingDeque全面解析!

Java并发基础:LinkedTransferQueue全面解析!

Java并发基础:LinkedBlockingQueue全面解析!

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

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

相关文章

Web安全研究(六)

文章目录 HideNoSeek: Camouflaging(隐藏) Malicious JavaScript in Benign ASTs文章结构Introjs obfuscationmethodologyExample HideNoSeek: Camouflaging(隐藏) Malicious JavaScript in Benign ASTs CCS 2019 CISPA 恶意软件领域&#xff0c;基于学习的系统已经非常流行&am…

嵌入式Qt 计算器界面设计代码重构

一.计算器界面设计代码重构 计算器界面设计&#xff1a;嵌入式Qt 计算器界面设计-CSDN博客 重构的概念&#xff1a; 代码实现与代码重构的不同&#xff1a; 软件开发过程&#xff1a; 什么样的代码需要重构&#xff1a; 计算器界面代码重构的框架设计&#xff1a; 实验&#…

实现JNDI

实现JNDI 问题陈述 Smart Software Developer Ltd.想要开发一款Web应用程序,它使用servlt基于雇员ID显示雇员信息,雇员ID由用户通过HTML用户界面传递。雇员详细信息存储在Employee_Master表中。另外,Web应用程序应显示网站被访问的次数。 解决方案 要解决上述问题,需要执…

福布斯2023年推荐:十佳项目管理软件榜单揭晓

项目管理软件可以轻松规划项目、分配任务并保持团队井井有条&#xff0c;以便满足截止日期和目标。然而当今市场上有如此多的项目管理系统&#xff0c;选择适合您需求的正确选项可能很困难。为了提供帮助&#xff0c;福布斯小型企业顾问团队分析了数十家领先的提供商&#xff0…

【医学知识图谱 自动补全 关系抽取】生成模型 + 医学知识图谱 = 发现三元组隐藏的关系实体对

生成模型 医学知识图谱 发现三元组新关系实体对 提出背景问题&#xff1a;如何自动发现并生成医疗领域中未被标注的实体关系三元组&#xff1f;CRVAE模型 提出背景 论文&#xff1a;https://dl.acm.org/doi/pdf/10.1145/3219819.3220010 以条件关系变分自编码器&#xff08;…

【通讯录案例-归档解档 Objective-C语言】

一、接下来,我们来说这个“归档”、“解档”、 1.归档、解档、这一块儿呢 首先呢,我们这个目标啊,还是跟“代理”差不多,要会用, 一会儿给大家画一幅图,让大家去了解“归档”、“解档”、每一句话,到底都干了什么, 好,我们先新建一个项目, 新建一个项目 新建一个…

Python实现EMV指标计算:股票技术分析的利器系列(2)

Python实现EMV指标计算&#xff1a;股票技术分析的利器系列&#xff08;2&#xff09; 介绍算法解释&#xff1a; 核心代码&#xff1a;rolling函数介绍 完整代码&#xff1a;一定要看 介绍 先看看官方介绍&#xff1a; EMV(简易波动指标&#xff09; 用法 1.EMV 由下往上穿越…

“操作符大揭秘:一篇文章让你秒懂所有!”

目录 1. ⼆进制介绍 2. 原码、反码、补码 3. 移位操作符 4. 位操作符&#xff1a;&、|、^ 5. 逗号表达式 6. 下标访问[]、函数调⽤() 7. 操作符的属性&#xff1a;优先级、结合性 8. 整型提升 9. 算术转换 10. 表达式求值 正文开始&#xff1a; 1. ⼆进制 其实我…

Leetcode 1035 不相交的线

题意理解&#xff1a; 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足满足&#xff1a; nums1[i] nums2[j]且绘制的直线不与任何其他连线&#xff…

MATLAB|【免费】高比例可再生能源电力系统的调峰成本量化与分摊模型

目录 主要内容 部分代码 结果一览 下载链接 主要内容 程序复现文献《高比例可再生能源电力系统的调峰成本量化与分摊模型》&#xff0c;从净负荷波动的角度出发&#xff0c;建立了调峰成本的量化与分摊模型&#xff0c;构造了无调峰需求的替代场景&#xff0c;将…

阿里云游戏服务器一年费用多少?

阿里云游戏服务器租用价格表&#xff1a;4核16G服务器26元1个月、146元半年&#xff0c;游戏专业服务器8核32G配置90元一个月、271元3个月&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云游戏专用服务器详细配置和精准报价&#xff1a; 阿里云游戏服务器租用价格表 阿…

【JavaEE进阶】 图书管理系统开发日记——陆

文章目录 &#x1f38b;前言&#x1f343;删除图书&#x1f6a9;约定前后端交互接口&#x1f6a9;完善前端代码&#x1f6a9;接口测试 &#x1f38d;批量删除&#x1f6a9;约定前后端交互接口&#x1f6a9;实现后端服务器代码&#x1f388;控制层&#x1f388;业务层&#x1f3…

Uipath 调用Python 脚本程序详解

Python 活动概述 UiPath.Python.Activities 是一个新的活动包&#xff0c;创建它是为了支持直接从工作流运行 Python 脚本和方法。 其包含以下活动&#xff1a; Python 作用域(Python Scope) - 为 Python 活动提供作用域的容器。 加载 Python 脚本(Load Python Script) - 将 P…

Servlet JSP-Eclipse安装配置Maven插件

Maven 是一款比较常用的 Java 开发拓展包&#xff0c;它相当于一个全自动 jar 包管理器&#xff0c;会导入用户开发时需要使用的相应 jar 包。使用 Maven 开发 Java 程序&#xff0c;可以极大提升开发者的开发效率。下面我就跟大家介绍一下如何在 Eclipse 里安装和配置 Maven 插…

(一)【Jmeter】JDK及Jmeter的安装部署及简单配置

JDK的安装和环境变量配置 对于Linux、Mac和Windows系统&#xff0c;JDK的安装和环境变量配置方法略有不同。以下是针对这三种系统的详细步骤&#xff1a; 对于Linux系统&#xff1a; 下载适合Linux系统的JDK安装包&#xff0c;可以选择32位或64位的版本。 将JDK的安装包放置…

ubuntu服务器部署gitlab docker并配置nginx反向代理https访问

拉取镜像 docker pull gitlab/gitlab-ce运行容器 docker run --detach \--publish 9080:80 --publish 9022:22 --publish 9443:443\--namegitlab \--restartalways \--volume /home/docker/gitlab/config:/etc/gitlab \--volume /home/docker/gitlab/logs:/var/log/gitlab \-…

今天:旧时是这样“破五迎福”

昨&#xff08;正月初四&#xff09;天&#xff0c;笔者——“ 人民体验官 ”&#xff0c; 为了推广人民日报官方微博文化产品所发表在10余个网站自媒体平台上的文章《今天&#xff1a;大年初四迎灶神爷》&#xff0c;不知何故被笔者寄居养老城市的自媒体论坛反复拒之门外&…

Spring Boot3自定义异常及全局异常捕获

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途。 目录 前置条件 目的 主要步骤 定义自定义异常类 创建全局异常处理器 手动抛出自定义异常 前置条件 已经初始化好一个…

使用一根网线,让Ubuntu和正点原子I.MX6ULL开发板互相ping通

1.硬件准备 准备一根网线即可 2. 让windows和I.MX6ULLping通 2.1 找根网线将I.MX6ULL和电脑连起来 2.2 让I.MX6ULL通电运行起来&#xff0c;我这里使用的是正点原子版本的内核、 2.3 进入电脑的网络连接后&#xff0c;按照如下步骤操作 2.4 将ip地址、子网掩码、默认网关…

情人节html代码

一、一个带有心形和祝福消息的页面 如果想在网页上创建一个简单的情人节祝福&#xff0c;可以使用HTML和CSS。以下是一个简单的例子&#xff0c;它创建了一个带有心形和祝福消息的页面&#xff1a; <!DOCTYPE html> <html> <head> <title>情人节…