Java 线程安全的集合
Vector
ArrayList 的线程安全版本,对所有的修改方法都进行了 synchronized 同步处理。适用于多线程环境下对数据一致性要求高,且读写操作相对比较均衡,不需要很高并发性能的场景。由于所有操作都进行同步,在高并发环境下,性能相对较差
Hashtable
HashMap 的线程安全版本,对每个关键方法都进行了 synchronized 同步处理。在多线程环境下需要一个线程安全的键值对存储结构,并且对数据的读写频率相对均衡时适用。高并发下性能不佳,并且不允许键或值为 null
ConcurrentHashMap
1. 数据结构
在 JDK1.7 中,ConcurrentHashMap 底层数据结构由多个 Segment 组成,Segment 继承自 ReentrantLock,本质上是一个可重入锁,每个 Segment 独立管理一部分数据,相当于一个小型的哈希表。每个 Segment 内部包含一个 HashEntry 数组,用于存储键值对。HashEntry 是一个链表结构,用于解决哈希冲突,新的键值对会插入到链表头部
在 JDK1.8 中,ConcurrentHashMap 底层数据结构是一个 Node 数组,Node 节点用链表或红黑树解决哈希冲突。当链表长度小于等于 8 时,采用链表存储数据。当链表长度大于 8 且数组长度大于 64 时,链表会转化为红黑树
2. 读取原理
在 JDK1.7 中,HashEntry 的 value 和 next 指针都被声明为 volatile 类型,保证内存可见性。不同的 Segment 可以并发访问,多个线程可以同时读取不同 Segment 的数据
在 JDK1.8 中,Node 节点的 value 和 next 指针都被声明为 volatile 类型,保证内存可见性。多个线程可同时读取不同位置的元素,提高并发读性能
3. 写入原理
在 JDK1.7 中,首先需要根据键的哈希值定位到对应的 Segment,然后获取该 Segment的锁。获取锁后,在对应 Segment 的 HashEntry 数组中找到合适的位置,将新的键值对插入到链表头部
在 JDK1.8 中,首先需要根据键的哈希值定位到对应的 Node 数组索引,若该位置为空,利用 CAS 操作将新节点插入,成功则插入完成。若 CAS 插入失败,说明发生哈希冲突。当发生哈希冲突时,若是链表,则对链表头节点加锁,遍历链表插入或更新,若是红黑树,则对红黑树的根节点加锁,按红黑树规则插入或更新
4. 扩容机制
在 JDK1.7 中,当某个 Segment 的 HashEntry 数组的元素数量达到阈值时,该 Segment 会加锁进行扩容操作。扩容时会创建一个新的更大的 HashEntry 数组,然后将原数组中的元素重新哈希并复制到新数组。不同的 Segment 可以独立进行扩容,不会影响其他 Segment 的正常操作
在 JDK1.8 中,当元素数量达到阈值,触发扩容。首先使用 CAS 操作创建一个更大的 Node 数组,然后使用 CAS 操作更新数组中的 Node 节点引用,再对 Node 节点的链表头节点或红黑树根节点使用 synchronized 关键字加锁,进行数据迁移操作。ConcurrentHashMap 采用多线程分段迁移的方式将原数组元素迁移到新数组,不同线程可负责不同段的迁移工作
CopyOnWriteArrayList / CopyOnWriteArraySet
CopyOnWriteArrayList 是一种线程安全的 List 实现,允许在多线程环境下进行并发的读写操作,其核心思想是“写时复制”,即当进行写操作时,会创建一个原数据结构的副本,在副本上进行修改,完成后再将副本替换原数据结构。CopyOnWriteArraySet 与 CopyOnWriteArrayList 的作用与实现类似
当执行写操作时,CopyOnWriteArrayList 会先创建一个当前数组的副本,对副本进行写操作。由于操作的是副本,不会影响到其他线程对原数组的读操作,从而保证了读写之间的并发安全。完成对副本的写操作后,会通过原子操作将原数组的引用替换为指向新的副本数组的引用。在这个替换过程中,使用 volatile 关键字修饰数组引用,保证其他线程能够及时看到更新后的数组。上述整个过程使用 ReentrantLock 锁保证同一时刻只有一个线程能够进行写操作
ConcurrentLinkedQueue
ConcurrentLinkedQueue 是基于链表实现的线程安全队列,采用 CAS 算法实现无锁的并发访问。比如多个线程同时进行出队操作时,每个线程都可以独立地尝试更新头节点的引用,通过 CAS 操作确保只有一个线程能够成功更新,从而实现了无锁的并发访问
BlockingQueue
BlockingQueue 是阻塞队列,内部使用锁机制实现线程安全。当队列已满时,尝试向队列中添加元素的线程会被阻塞,直到队列有空间可用。当队列为空时,尝试从队列中获取元素的线程会被阻塞,直到队列有元素可获取
具体参考:https://www.cnblogs.com/Yee-Q/p/14580034.html
同步包装器
Java 同步包装器是指通过 Collections 类的静态方法将非线程安全的集合转换为线程安全的集合,主要包括以下几种:
- synchronizedList:把普通的 List 转换为线程安全的列表。通过对 List 的所有操作添加同步锁,确保同一时刻只有一个线程能够访问列表,避免并发访问时出现数据不一致等问题,如
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>()); - synchronizedMap:把普通的 Map 转换为线程安全的映射。在对 Map 进行操作时,都会进行同步处理,保证多线程环境下 Map 的操作安全,如:
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>()); - synchronizedSet:把普通的 Set 转换为线程安全的集合。通过同步机制,确保在多线程访问 Set 时,元素的添加、删除等操作不会出现并发问题,如:
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
Java 线程安全的集合的更多相关文章
- Java——线程安全的集合
线程安全的集合 java.util.concurrent包:ConcurrentHashMap,ConcurrentSkipListMap,ConcurrentSkipListSet,Concu ...
- java利用线程池处理集合
java利用线程池处理集合 2018年07月23日 17:21:19 衍夏成歌 阅读数:866 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/s ...
- java 多线程 线程安全及非线程安全的集合对象
一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到该线程读取完之后,其他线程才可以使用.防止出现数据不一致或 ...
- java 中如何声明线程安全的集合 set, map 和list
线程安全的集合 http://blog.sina.com.cn/s/blog_508938e10102v1ig.html //make thread-safe list List MyStrList ...
- java 中如何声明线程安全的集合 set, map 和list【转】
线程安全的集合 引用自 http://blog.sina.com.cn/s/blog_508938e10102v1ig.html //make thread-safe list List MyStrL ...
- Java中线程安全的集合
如果多线程并发的访问与一个数据结构,那么很容易破坏一个数据结构. 例如,一个线程可能要向一个散列表中插入一条数据的过程中,被剥夺了控制权.如果另外一个线程也开始遍历同一个链表,很可能造成混乱,抛出异常 ...
- Java基础面试:集合、内部类、线程
package test; import java.util.Hashtable; import java.util.Map; public class test { public static St ...
- Java多线程理解:线程安全的集合对象
1.概念介绍 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用.不会出现数据不一致或者数据污染. 线程不安全就 ...
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
- 【转载】 Java线程面试题 Top 50
Java线程面试题 Top 50 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的J ...
随机推荐
- 加入security+jwt安全策略
Pom中引入 <!-- security --> <dependency> <groupId>org.springframework.boot</groupI ...
- 基于Three.js的大屏3D地图(一)
依赖安装 yarn add three yarn add @types/three yarn add d3-geo three库安装后在node_modules下其还包含核心three/src和插件t ...
- 从零开始构建一个基于大模型和 RAG 的知识库问答系统
SimpleAbdQA 本项目所使用的大模型为:qwen1.8b 演示中所使用Embedding为:Word2vec 一.介绍 通过从本项目中,你可以得到: 了解基于大模型的本地知识库的运作原理 了解 ...
- 23种设计模式实战:重学Java设计模式
23种设计模式实战PDF: 链接:https://pan.baidu.com/s/1XfjkBt19G7jZQfwk5wAV3w 提取码:1234
- 《AutoCAD2020中文版基础教程》和《从零开始—AutoCAD 2020中文版基础教程》配套资源下载
<AutoCAD2020中文版基础教程>作者:姜春峰//武小紅//魏春雪中国青年出版社配套资源链接:https://pan.baidu.com/s/1kPGNKZEw2kOTGqZyXjp ...
- [LC1161]最大层内元素和
题目概述 给你一个二叉树的根节点 root.设根节点位于二叉树的第 1 层,而根节点的子节点位于第 2 层,依此类推. 请返回层内元素之和 最大 的那几层(可能只有一层)的层号,并返回其中 最小 的那 ...
- 利用SDCC开源项目搭建C51编译平台
下载sdcc 安装sdcc 安装sublime 新建编译系统输入以下内容 { "shell_cmd": "sdcc \"${file}\" " ...
- Solution Set -「NOIP Simu.」20221005
\(\mathscr{A}\sim\)「CF 1252G」Performance Review Link & Submission. Tag:「水题无 tag」 记 \(A=a_1 ...
- 一个超经典 WinForm,WPF 卡死问题的终极反思
一:背景 1. 讲故事 写这篇文章起源于训练营里一位朋友最近在微信聊到他对这个问题使用了一种非常切实可行,简单粗暴的方式,并且也成功解决了公司里几个这样的卡死dump,如今在公司已是灵魂级人物,让我也 ...
- WPF 无边框窗体改变大小和移动
WIN32 API: private const int WM_NCHITTEST = 0x0084; private readonly int agWidth = 12; //拐角宽度 privat ...