聊聊并发编程——并发容器和阻塞队列
目录
一.ConcurrentHashMap
1.为什么要使用ConcurrentHashMap?
2.ConcurrentHashMap的类图
3.ConcurrentHashMap的结构图
二.阻塞队列
Java中的7个阻塞队列
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。可以指定容量也可以无界。
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。 用于按照指定延迟时间对元素进行排序的阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。常用于线程间的手递手传递。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
一.ConcurrentHashMap
1.为什么要使用ConcurrentHashMap?
-
不安全的HashMap
Java 中的
HashMap是非线程安全的,这意味着如果多个线程同时访问和修改同一个HashMap实例,可能会导致不一致的结果或抛出异常。以下是一个示例代码,展示了HashMap的线程不安全行为:
public static void main(String[] args) {// 创建一个 HashMapMap<Integer, String> map = new HashMap<>();// 创建一个线程池ExecutorService executorService = Executors.newFixedThreadPool(2);// 向 HashMap 中添加键值对的任务Runnable task = () -> {for (int i = 0; i < 1000; i++) {map.put(i, "Value " + i);}};// 启动两个线程同时执行添加任务executorService.submit(task);executorService.submit(task);// 等待线程池执行完毕executorService.shutdown();// 等待一段时间以确保线程池完成try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// 输出 HashMap 的大小System.out.println("HashMap size: " + map.size());}
在上述示例中,我们创建了一个包含两个线程的线程池,并让它们同时向同一个 HashMap 实例中添加键值对。由于 HashMap 不是线程安全的,这样的并发写入操作可能导致不一致的结果。在某些情况下,可能会抛出 ConcurrentModificationException 异常。
-
效率低下的HashTable
HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable 的效率非常低下。因为当一个线程访问HashTable的同步方法,其他线程也访问HashTable的同 步方法时,会进入阻塞或轮询状态。如线程1使用put进行元素添加,线程2不但不能使用put方 法添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低。
-
ConcurrentHashMap的锁分段技术可有效提升并发访问率
HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的 线程都必须竞争同一把锁。
ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段地存 储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
2.ConcurrentHashMap的类图

3.ConcurrentHashMap的结构图

二.阻塞队列
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。
-
支持阻塞的插入方法:当队列满时,会阻塞插入元素的线程,直到队列不满。
-
支持阻塞的移除方法:当队列为空时,获取元素的线程会等待队列变为非空。
阻塞队列常用于生产者和消费者场景。生产者就是添加元素的线程,消费者就是获取元素的线程,阻塞队列就是生产者存放元素、消费者获取元素的容器。插入和移除操作的4中处理方式:
| 方法/处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
|---|---|---|---|---|
| 插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
| 移除方法 | remove() | poll() | take() | poll(time,unit) |
| 检查方法 | element() | peek() | 不可用 | 不可用 |
-
抛出异常:当队列满了,再插入元素时,会抛出IllegalStateException(“Queue full”)异常。当队列为空,再获取元素,会抛出NoSuchElementException异常。
-
返回特殊值:当队列插入元素时,会返回元素是否插入成功,成功返回true。如果是移除方法,则是从队列去除元素,如果不存在则返回null。
-
一直阻塞:当队列满时,往队列put元素,队列会一直阻塞添加元素的线程,知道队列可用或者响应中断退出。当队列为空时,如果从队列中take元素,队列会阻塞获取元素的线程,知道队列不为空。
-
超时退出:当队列满时,如果插入元素,队列会阻塞插入元素的线程一段时间,超过了指定时间,线程就会退出。
Java中的7个阻塞队列
如果是无界阻塞队列,队列不可能会出现满的情况,所以使用put或offer方法永 远不会被阻塞,而且使用offer方法时,该方法永远返回true。
-
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
public class ArrayBlockingQueueExample {public static void main(String[] args) throws InterruptedException {ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue(5); // 生产者Thread produce = new Thread(()->{try {for (int i = 0; i < 10; i++) {arrayBlockingQueue.put(i);System.out.println("produced:" + i);}} catch (InterruptedException e) {e.printStackTrace();}}); // 消费者Thread consume = new Thread(()->{try {while (true) {int i = arrayBlockingQueue.take();System.out.println("consumed" + i);}} catch (InterruptedException e) {e.printStackTrace();}}); produce.start();consume.start(); Thread.sleep(2000);produce.interrupt();consume.interrupt();} } -
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。可以指定容量也可以无界。
public class LinkedBlockingQueueExample {public static void main(String[] args) throws InterruptedException {LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue(5); // 同上,创建生产者和消费者线程并启动 // 主线程等待,中断生产者和消费者Thread.sleep(2000);produce.interrupt();consume.interrupt();} } -
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
public class PriorityBlockingQueueExample {public static void main(String[] args) throws InterruptedException {PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(); // 同上,创建生产者和消费者线程并启动 // 主线程等待,中断生产者和消费者Thread.sleep(2000);produce.interrupt();consume.interrupt();} } -
DelayQueue:一个使用优先级队列实现的无界阻塞队列。 用于按照指定延迟时间对元素进行排序的阻塞队列。
public class DelayQueueExample {public static void main(String[] args) throws InterruptedException {DelayQueue<DelayedElement > queue = new DelayQueue<DelayedElement >(); // 创建消费者线程Thread consume = new Thread(() ->{try {while (true) {DelayedElement element = queue.take();System.out.println("consume:" + element.getValue());}} catch (InterruptedException e) {e.printStackTrace();}});consume.start(); // 生产者添加元素queue.put(new DelayedElement("value 5", 1, TimeUnit.SECONDS));queue.put(new DelayedElement("value 4", 2, TimeUnit.SECONDS));queue.put(new DelayedElement("value 3", 3, TimeUnit.SECONDS));queue.put(new DelayedElement("value 2", 4, TimeUnit.SECONDS));queue.put(new DelayedElement("value 1", 5, TimeUnit.SECONDS)); Thread.sleep(10000); consume.interrupt();} static class DelayedElement implements Delayed {private String value;private long delayTime; public String getValue() {return value;} public DelayedElement(String value, long delayTime, TimeUnit timeUnit) {this.value = value;this.delayTime = System.currentTimeMillis() + timeUnit.toMillis(delayTime);} @Overridepublic long getDelay(TimeUnit unit) {return delayTime - System.currentTimeMillis();} @Overridepublic int compareTo(Delayed o) {return Long.compare(this.delayTime, ((DelayedElement) o).delayTime);}} } -
SynchronousQueue:一个不存储元素的阻塞队列。常用于线程间的手递手传递。
public class SynchronousQueueExample {public static void main(String[] args) throws InterruptedException {SynchronousQueue<Integer> queue = new SynchronousQueue<>();// 同上,创建生产者和消费者线程并启动 // 主线程等待,中断生产者和消费者Thread.sleep(2000);produce.interrupt();consume.interrupt();} } -
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
public class LinkedTransferQueueExample {public static void main(String[] args) throws InterruptedException {LinkedTransferQueue<Integer> queue = new LinkedTransferQueue<>(); // 同上,创建生产者和消费者线程并启动 // 主线程等待,中断生产者和消费者Thread.sleep(2000);produce.interrupt();consume.interrupt();} } -
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
public class LinkedBlockingDequeExample {public static void main(String[] args) throws InterruptedException {LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>(); // 同上,创建生产者和消费者线程并启动 // 主线程等待,中断生产者和消费者Thread.sleep(2000);produce.interrupt();consume.interrupt();} }
相关文章:
聊聊并发编程——并发容器和阻塞队列
目录 一.ConcurrentHashMap 1.为什么要使用ConcurrentHashMap? 2.ConcurrentHashMap的类图 3.ConcurrentHashMap的结构图 二.阻塞队列 Java中的7个阻塞队列 ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。 LinkedBlockingQueue…...
我庄严承诺终生不去承德旅游
虽然人微言轻,但也要尽一份力。 在此,我庄严承诺: 如果承德相关机构不返还那名"灵活就业人员"105.82万元的财产,并进行公开道歉。 我将终生不去承德旅游, 我将终生不买承德出产的任何产品。 我还将劝诫我…...
【python】python实现杨辉三角的三种方法
文章目录 1.杨辉三角介绍:2.方法一:迭代3.方法二:生成器4.方法三:递归 1.杨辉三角介绍: 杨辉三角是一种数学图形,由数字排列成类似三角形的形状。它的每个数值等于它上方两个数值之和。这个三角形的形状可以…...
GitHub 基本操作
最近要发展一下自己的 github 账号了,把以前的项目代码规整规整上传上去,这里总结了一些经验,经过数次实践之后,已解决几乎所有基本操作中的bug,根据下面的操作步骤来,绝对没错了。(若有其他问题…...
Docker和Docker compose的安装使用指南
一,环境准备 Docker运行需要依赖jdk,所以需要先安装一下jdk yum install -y java-1.8.0-openjdk.x86_64 二,Docker安装和验证 1,安装依赖工具 yum install -y yum-utils 2,设置远程仓库 yum-config-manager --add-r…...
51单片机控制电动机正反转,PWM调速,记录转动圈数。
今天的实验需要用到的材料有:51单片机最小系统,4X4的矩阵键盘,DC直流6V-12V带编码器电机,L298N模块,一个led小灯。下面把产品截图展示一下: 单片机就不展示了,都一样,下面是接线图&a…...
JAVA学习(方法的定义和调用)
一、方法的定义和调用 1、关键词:static表示静态方法,如没有返回值使用void,方法名前使用类型,例如int、float等; /*** 测试方法的定义和调用*/public class TestMethod {public static void main(String[] args) {a…...
Linux(CentOS/Ubuntu)——安装nginx
如果确定你的系统是基于CentOS或RHEL,可以使用以下命令: ①、安装库文件 #安装gcc yum install gcc-c#安装PCRE pcre-devel yum install -y pcre pcre-devel#安装zlib yum install -y zlib zlib-devel#安装Open SSL yum install -y openssl openssl-de…...
26962-2011 高频电磁场综合水处理器技术条件
声明 本文是学习GB-T 26962-2011 高频电磁场综合水处理器技术条件. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了高频电磁场综合水处理器(以下简称处理器)的术语和定义、分类和型号、结构型式、 要求及检验、标志、包装和贮运…...
图扑软件受邀亮相 IOTE 2023 国际物联网展
IOTE 2023 国际物联网展,作为全球物联网领域的盛会,于 9 月 20 日 - 22 日在中国深圳拉开帷幕。本届展会以“IoT构建数字经济底座”为主题,由深圳市物联网产业协会主办,打造当前物联网最新科技大秀。促进物联网与各行业深度融合&a…...
C语言文件操作与管理
一、为什么使用文件 在我们前面练习使用结构体时,写通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了ÿ…...
蓝桥等考Python组别八级005
第一部分:选择题 1、Python L8 (15分) 运行下面程序,输出的结果是( )。 i 1 while i < 4: print(i, end ) i 1 1 2 30 1 2 31 2 3 40 1 2 3 4 正确答案:C 2、Python L8 &#…...
JUnit介绍
JUnit是用于编写和运行可重复的自动化测试的开源测试框架, 这样可以保证我们的代码按预期工作。JUnit可广泛用于工业和作为支架(从命令行)或IDE(如Eclipse)内单独的Java程序。 JUnit提供: 断言测试预期结果。 测试功能共享通用的测试数据。 测试套件轻…...
(高阶) Redis 7 第16讲 预热/雪崩/击穿/穿透 缓存篇
面试题 什么是缓存预热/雪崩/击穿/穿透如何做缓存预热如何避免或减少缓存雪崩穿透和击穿的区别?穿透和击穿的解决方案出现缓存不一致时,有哪些修补方案缓存预热 理论 将需要的数据提前加载到缓存中,不需要用户使用的过程中进行数据回写。(比如秒杀活动数据等) 方案 1.…...
(三) gitblit管理员手册
(一)gitblit安装教程 (二) gitblit用户使用教程 (三) gitblit管理员手册 目录 权限管理创建仓库时创建用户普通用户 管理员用户访问限制和访问权限仓库创建权限分配 Teams普通组管理员组 参考资料 权限管理 创建仓库时 选择指定的人员查看,克隆,推送 不允许fork 对应Anonymo…...
ESKF算法融合GNSS与IMU信息,航向角的偏差是如何逐渐影响到重力加速度g以及位置偏差的 CSDN gpt
1##############################ESKF算法融合GNSS与IMU信息,航向角的偏差是如何逐渐影响到重力加速度g以及位置偏差的 CSDN gpt 航向角的偏差会逐渐影响重力加速度和位置偏差。首先,航向角的偏差会影响重力加速度的测量值。在ESKF算法中,通过将IMU测…...
Java初始化大量数据到Neo4j中(二)
接Java初始化大量数据到Neo4j中(一)继续探索,之前用create命令导入大量数据发现太过耗时,查阅资料说大量数据初始化到Neo4j需要使用neo4j-admin import 业务数据说明可以参加Java初始化大量数据到Neo4j中(一),这里主要是将处理好的节点数据和…...
flink1.17安装
Flink1.17安装 官网地址: https://nightlies.apache.org/flink/flink-docs-release-1.17/zh//docs/try-flink/local_installation/ 安装jdk11 ps:只能安装openjdk11,昨天安装的oracle jdk17,结果怎么也运行不起来。 sudo apt …...
SLAM从入门到精通(gmapping建图)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 前面我们介绍了hector slam建图。相对而言,hector slam建图对数据的要求比较低,只需要lidar数据就可以建图了。但是hector …...
中国312个历史文化名镇及景区空间点位数据集
一部中华史,既是人类创造丰富物质财富的奋头史,又是与自然共生共存的和谐史不仅留存下悠久丰富的人文思想和情怀,还在各处镌刻下可流传的生活场景,历史文化名镇(以下简称:名镇)就是这样真实的历史画卷。“镇”是一方的政治文化中心…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
