【JavaEE】线程安全的集合类

文章目录
- 前言
- 多线程环境使用 ArrayList
- 多线程环境使用队列
- 多线程环境使用哈希表
- 1. HashTable
- 2. ConcurrentHashMap
前言
前面我们学习了很多的Java集合类,像什么ArrayList、Queue、HashTable、HashMap等等一些常用的集合类,之前使用这些都是在单线程中使用的,而如今我们学习了多线程之后就要考虑这些集合在多线程中使用是否会发生一些线程不安全的问题。
原来的集合类大部分都是线程不安全的,除了Vector、Stack、HashTable,是线程安全的(但是这些都不建议使用了,因为Java官方已经将这些集合类标记了,随时都有可能被删除),其他的集合类就是线程不安全的。
多线程环境使用 ArrayList
因为 ArrayList 在多线程环境下可能涉及到同时读和写的操作从而导致线程不安全。为了解决在使用 ArrayList 线程不安全的问题,有以下几种解决方法。
- 自己使用同步机制(使用 synchronized 或者 ReentrantLock 加锁等)
- Collections.synchronizedList(new ArrayList); 使用这个集合类就是在相关方法前面进行 synchronized 加锁
synchronizedList 是标准库提供的一个基于 synchronized 进行线程同步的 List.
synchronizedList 的关键操作上都带有 synchronized
- 使用 CopyOnWriteArrayList
CopyOnWriteArrayList 即写时复制容器,当进行写操作的时候,不是直接添加进容器中,而是先Copy复制出一个容器,将新元素添加进新容器中。当添加完成之后就将原容器的引用指向新容器。这样就可以保证在写的时候,不需要进行加锁,也可以读,并且读的数据是正确的。

CopyOnWriteArrayList 容器的优缺点:
优点:
- 线程安全:CopyOnWriteArrayList 是线程安全的,你可以在多线程环境中无需额外的同步或锁定就能使用它。
- 读操作无需锁定:由于它使用了写时复制策略,所以读取操作(例如 get 和 iterator)可以在没有锁定的情况下进行,这使得它非常适合读多写少的场景。
- 避免阻塞:CopyOnWriteArrayList 使用了一种称为 “乐观锁” 的策略,这意味着它不会在执行修改操作时阻塞读取操作。
缺点:
- 写操作开销大:每次修改操作都会复制整个底层数组,这在数组较大时可能会导致显著的性能开销。因此,CopyOnWriteArrayList 不适合写操作频繁的场景。
- 内存消耗大:由于每次写操作都会复制整个数组,这可能会导致大量的内存消耗。
- 迭代器一致性:CopyOnWriteArrayList 的迭代器是弱一致性的(weakly consistent)。这意味着如果你在迭代过程中修改了列表(除非是通过迭代器自己的 remove 方法),那么迭代器可能不会反映这些修改。
总的来说,CopyOnWriteArrayList 是一种非常有用的工具,但是你应该清楚它的适用场景:读多写少,且数据大小适中。如果你在一个写操作频繁或者数据非常大的场景中使用它,可能会遇到性能问题。
多线程环境使用队列
因为队列先进先出的特性,所以主要的线程安全问题就是当队列为空的时候读取和队列为满的时候插入,为了解决队列在多线程中会出现的问题,主要就是使用了阻塞队列的方法使队列为空时读取数据和队列为满时的插入数据操作进入阻塞等待状态。
- ArrayBlockingQueue
基于数组实现的阻塞队列 - LinkedBlockingQueue
基于链表实现的阻塞队列 - PriorityBlockingQueue
基于堆实现的带优先级的阻塞队列 - TransferQueue
最多只包含一个元素的阻塞队列
多线程环境使用哈希表
在多线程环境下使用哈希表可以使用:
- HashTable
- ConcurrentHashMap
1. HashTable
HashTable 只是给一些关键方法加上了 synchronized 锁,是直接加在方法上的,也就相当于给 HashTable 对象加锁。


对整个对象加锁就意味着:
- 如果多线程访问同一个 Hashtable 就会直接造成锁冲突.
- size 属性也是通过 synchronized 来控制同步, 也是比较慢的.
- 一旦触发扩容, 就由该线程完成整个扩容过程. 这个过程会涉及到大量的元素拷贝, 效率会非常低.

但是实际上,并不是多个线程只要访问同一个 HashTable 对象需要进行加锁,而是只有当两个线程访问到同一个链表的时候才需要进行加锁,并且哈希寻址的时候两个线程同时找到同一个链表的几率是比较小的,所以没必要将整个哈希表都进行加锁。
2. ConcurrentHashMap
ConcurrentHashMap 在 HashTable 的基础上做出了一系列的优化。
- ConcurrentHashMap 将 HashTable 的一个大锁转换为了一个一个加在锁桶的小锁,大大降低了锁冲突的现象。

- 充分利用 CAS 特性,避免了一些不必要加锁的情况。比如更改哈希表中的元素个数 size 时候就使用 CAS 原子操作,不进行加锁。
- ConcurrentHashMap 对读操作没有进行加锁,也就是说当多个线程同时进行读和读操作、读和写操作的时候都不会出现锁竞争的现象。
但是如果不对读操作进行加锁的话,会不会发生读到了一个只修改了一半的数据呢?
答:
其实是不会的,因为 ConcurrentHashMap 在底层编码的时候,比较谨慎的处理了一些细节。在修改操作的时候会避免使用++和- -的这些非原子性的操作,而是使用 = 这种原子性的操作。有了这些操作,就使得读取到的数据要么是修改之前的数据,要么是修改值时候的数据,不会出现读取到的数据是只修改了一半的数据。
- 优化了扩容方式: 化整为零。通过这个优化就避免了短时间内因哈希表中元素过多进行扩容的时候,需要改变的元素过多而造成计算机需要承担的负担过重而导致阻塞的情况。
- 发现需要扩容的线程, 只需要创建一个新的数组, 同时只搬几个元素过去.
- 扩容期间, 新老数组同时存在.
- 后续每个来操作 ConcurrentHashMap 的线程, 都会参与搬家的过程. 每个操作负责搬运一小部分元素.
- 搬完最后一个元素再把老数组删掉.
- 这个期间, 插入只往新数组加.
- 这个期间, 查找需要同时查新数组和老数组
相关文章:
【JavaEE】线程安全的集合类
文章目录 前言多线程环境使用 ArrayList多线程环境使用队列多线程环境使用哈希表1. HashTable2. ConcurrentHashMap 前言 前面我们学习了很多的Java集合类,像什么ArrayList、Queue、HashTable、HashMap等等一些常用的集合类,之前使用这些都是在单线程中…...
【C++算法】is_partitioned、partition_copy和partition_point
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、is_partitioned函数:1.1 is_partitioned是什么?1.2 函数原型1.3 示例代码1.4 更多示例代码 二、partition_copy函数2.1 概念2.2 函数…...
MyBatis(JavaEE进阶系列4)
目录 前言: 1.MyBatis是什么 2.为什么要学习MyBatis框架 3.MyBatis框架的搭建 3.1添加MyBatis框架 3.2设置MyBatis配置 4.根据MyBatis写法完成数据库的操作 5.MyBatis里面的增删改查操作 5.1插入语句 5.2修改语句 5.3delete语句 5.4查询语句 5.5like查…...
『力扣每日一题15』:买卖股票的最佳时机
一、题目 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的…...
Java中栈实现怎么选?Stack、Deque、ArrayDeque、LinkedList(含常用Api积累)
目录 Java中的Stack类 不用Stack有以下两点原因 1、从性能上来说应该使用Deque代替Stack。 2、Stack从Vector继承是个历史遗留问题,JDK官方已建议优先使用Deque的实现类来代替Stack。 该用ArrayDeque还是LinkedList? ArrayDeque与LinkList区别࿱…...
雷达分辨率单元、单向/双向雷达方程、天气雷达方程简介
一、点状目标 如果两个点状目标在一个分辨率单元中,经典脉冲雷达只能看到一个目标。 点状目标 二、雷达距离分辨率 对于简单的键控开/关脉冲调制: 对于使用脉冲内调制的雷达,距离分辨率取决于压缩脉冲的脉冲持续时间。脉冲压缩比(PCR)取决于传输带宽BWtx,即距离分辨率取…...
RabbitMQ之Fanout(扇形) Exchange解读
目录 基本介绍 适用场景 springboot代码演示 演示架构 工程概述 RabbitConfig配置类:创建队列及交换机并进行绑定 MessageService业务类:发送消息及接收消息 主启动类RabbitMq01Application:实现ApplicationRunner接口 基本介绍 Fa…...
Redisson—分布式集合详述
7.1. 映射(Map) 基于Redis的Redisson的分布式映射结构的RMap Java对象实现了java.util.concurrent.ConcurrentMap接口和java.util.Map接口。与HashMap不同的是,RMap保持了元素的插入顺序。该对象的最大容量受Redis限制,最大元素数…...
开发做前端好还是后端好?这是个问题!
前言 随着互联网的快速发展,越来越多的人选择从事Web开发行业,而Web开发涉及到前端和后端两个方面,相信许多人都曾经对这两个方面进行过探究。而且编程世界就像一座大城市,前端开发和后端开发就像城市的两个不同街区。作为初学者&…...
运行huggingface Kosmos2报错 nameerror: name ‘kosmos2tokenizer‘ is not defined
尝试运行huggingface上的Kosmos,https://huggingface.co/ydshieh/kosmos-2-patch14-224失败,报错: nameerror: name kosmos2tokenizer is not defined查看报错代码: vi /root/.cache/huggingface/modules/transformers_modules/ydshieh/kosmos-2-patch14-224/48e3edebaeb…...
吃鸡玩家必备神器!一站式提升战斗力、分享干货!
大家好,我是吃鸡玩家。在这个视频中,我要分享一个让你瞬间提高战斗力的神器,同时让你享受到顶级游戏作战干货的盛宴!让我们一起来了解吧! 首先,我们推荐绝地求生作图工具。通过这款工具,你可以轻…...
【maven】idea中基于maven-webapp骨架创建的web.xml问题
IDEA中基于maven-webapp骨架创建的web工程,默认的web.xml是这样的。 <!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name…...
【算法题】2034. 股票价格波动
插: 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 坚持不懈,越努力越幸运,大家一起学习鸭~~~ 题目: 给你一支股票价格的数据流。数据流…...
APSIM模型】作物模型应用案例
APSIM (Agricultural Production Systems sIMulator)模型是世界知名的作物生长模拟模型之一。APSIM模型有Classic和Next Generation两个系列模型,能模拟几十种农作物、牧草和树木的土壤-植物-大气过程,被广泛应用于精细农业、水肥管理、气候变化、粮食安…...
io_uring之liburing库安装
手动编译和安装 liburing: 1.首先,从 liburing 的 GitHub 仓库中获取源代码。您可以使用以下命令克隆仓库: git clone https://github.com/axboe/liburing.git2.进入 liburing 目录: cd liburing3.运行configure ./configure …...
Python WebSocket自动化测试:构建高效接口测试框架!
为了更高效地进行WebSocket接口的自动化测试,我们可以搭建一个专门的测试框架。本文将介绍如何使用Python构建一个高效的WebSocket接口测试框架,并重点关注以下四个方面的内容:运行测试文件封装、报告和日志的封装、数据驱动测试以及测试用例…...
MySQL数据库——SQL优化(1)-介绍、插入数据、主键优化
目录 介绍 插入数据 Insert 大批量插入数据 主键优化 数据组织方式 页分裂 页合并 索引设计原则 介绍 SQL优化将分为下面几个部分进行学习: 插入数据主键优化order by优化group by优化limit优化count优化update优化 首先就先来看第一方面, 插…...
Flink---10、处理函数(基本处理函数、按键分区处理函数、窗口处理函数、应用案例TopN、侧输出流)
星光下的赶路人star的个人主页 我的敌手就是我自己,我要他美好到能使我满意的程度 文章目录 1、处理函数1.1 基本处理函数(ProcessFunction)1.1.1 处理函数的功能和使用1.1.2 ProcessFunction解析1.1.3 处理函数的分类 1.2 按键分区处理函数&…...
多种方案教你彻底解决mac npm install -g后仍然不行怎么办sudo: xxx: command not found
问题概述 某些时候我们成功执行了npm install -g xxx,但是执行完成以后,使用我们全局新安装的包依然不行,如何解决呢? 解决方案1: step1: 查看npm 全局文件安装地址 XXXCN_CXXXMD6M ~ % npm list -g …...
斐波那契数列 JS
问题: 给出一个数字,找出它是斐波那契数列中的第几个数 斐波那契数列 [1, 1, 2, 3, 5, 8, 13, ...],后一个数字是前两个数字之和 输入的数字大于等于 2 如果输入数字不存于斐波那契数列中,返回 -1 function demo(num) {//初始数据…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
