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 ...
随机推荐
- Qt编写地图综合应用13-获取边界点
一.前言 获取边界点一般和行政区划搭配起来使用,比如用户输入一个省市的名称,然后自动定位到该省市,然后对该轮廓获取所有边界点集合输出到js文件,最后供离线使用,获取边界点还有一个功能就是获取当前区域内 ...
- Qt编写安防视频监控系统35-onvif抓拍图片
一.前言 以前不知道onvif也可以做抓拍功能,直到近期重新用Onvif Device Test Tool工具测试的时候,发现还有抓图的接口,于是抓跑分析出要收发的数据,然后加入到自己封装的onvif ...
- k8s集群部署项目完整过程记录
将k8s-web项目制作成镜像 构建 study/k8s-web:1.0 镜像 构建java环境并上传jar包 docker pull java:8u111 mkdir /usr/dockerfile ...
- WxPython跨平台开发框架之图标选择界面
在使用 wxPython 开发跨平台桌面应用程序时,创建一个图标选择界面通常用于让用户从图标资源库中选择图标,我们可以把图标分为自定义的图标资源和系统的图标资源两大类,最终我们把它们整合一起使用,在框 ...
- 修改led-core.c 让led的delay_on和delay_off时间不会应为trigger配置改版而重置为1HZ
先列一下leds trigger的设置流程 echo none > trigger 的流程 led_trigger_set() | led_stop_software_blink() echo ...
- 前端vue项目本地运行内网访问的方法
有时候在公司里,领导想在内网访问到你本地的项目,所以就有了前端项目内网访问 (1)首先在package文件里面的host改成0.0.0.0,像这个样子 (2)其次在config下的index.js里的 ...
- Arcgis加载Geoserver矢量切片
原帖地址 洒家废物 - Arcgis加载Geoserver矢量切片 准备点线面图层并发布图层组 此处我准备了石家庄市的县界名称(点).高速公路(线).县界(面),依次发布geoserver服务,创建图 ...
- 初识MinIO
writer:zgx last modify: 2020年09月26日 目录 前言 MinIO简介 MinIO使用 MinIO安装 MinIO纠删码 纠删码ensure code是什么 refers ...
- 重写equals()方法(idea生成的高效方法)
equals 方法Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象.在 Object 类中,这个方法将判断两个对象是否具有相同的引用.如果两个对象具有相同的引用, 它们一定 ...
- Docker的启停与配置等
Docker测试题 一.选择题(每题5分) 1.关于Docker 安装的表述错误的是(C) A.Docker支持在Windows.Linux.MacOS等系统上安装 B.CentOS安装Docker有 ...