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 线程安全的集合的更多相关文章

  1. Java——线程安全的集合

    线程安全的集合    java.util.concurrent包:ConcurrentHashMap,ConcurrentSkipListMap,ConcurrentSkipListSet,Concu ...

  2. java利用线程池处理集合

    java利用线程池处理集合 2018年07月23日 17:21:19 衍夏成歌 阅读数:866   版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/s ...

  3. java 多线程 线程安全及非线程安全的集合对象

    一.概念: 线程安全:就是当多线程访问时,采用了加锁的机制:即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到该线程读取完之后,其他线程才可以使用.防止出现数据不一致或 ...

  4. java 中如何声明线程安全的集合 set, map 和list

    线程安全的集合 http://blog.sina.com.cn/s/blog_508938e10102v1ig.html //make thread-safe list List MyStrList ...

  5. java 中如何声明线程安全的集合 set, map 和list【转】

    线程安全的集合 引用自 http://blog.sina.com.cn/s/blog_508938e10102v1ig.html //make thread-safe list List MyStrL ...

  6. Java中线程安全的集合

    如果多线程并发的访问与一个数据结构,那么很容易破坏一个数据结构. 例如,一个线程可能要向一个散列表中插入一条数据的过程中,被剥夺了控制权.如果另外一个线程也开始遍历同一个链表,很可能造成混乱,抛出异常 ...

  7. Java基础面试:集合、内部类、线程

    package test; import java.util.Hashtable; import java.util.Map; public class test { public static St ...

  8. Java多线程理解:线程安全的集合对象

    1.概念介绍 线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用.不会出现数据不一致或者数据污染. 线程不安全就 ...

  9. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  10. 【转载】 Java线程面试题 Top 50

    Java线程面试题 Top 50 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的J ...

随机推荐

  1. Linux新用户登录时出现“-bash-4.2$”的解决办法

    Linux服务器新建的用户在登录时显示"-bash-4.2$",而不是"user@hostname"的显示方式,出现此问题的原因是在添加普通用户时,用户家目录下 ...

  2. Linux系统手动安装Firefox浏览器

    大多数Linux发行版都以Firefox作为默认的浏览器,并可以轻松地从软件库中安装.例如:Debian/Ubuntu: sudo apt-get install firefoxFedora: sud ...

  3. Linux 中的内存使用率计算方式

    背景介绍 在工作中处理问题的时候,遇到一个问题,自己根据 top 命令时查看到的 used 和 total 计算出来的内存使用率已经达到 90% 以上了,但是系统自带的监控软件计算出来的使用率往往没有 ...

  4. 使用Matlab进行图像分类

    Classify images Instructions are in the task pane to the left. Complete and submit each task one at ...

  5. 使用R语言可视化地图数据

    在科研工作中,经常会遇到需要在地图上展示数据的情况,今天分享一下如何使用R语言可视化地图数据 # 先安装和导入R包,这里使用了本地地图数据,所以只导入maps包即可 # install.package ...

  6. 深入解析 Spring AI 系列:项目结构一览

    从今天起,我们将以 Spring AI 为主线,开始更新一系列的文章.这些文章将围绕 Spring AI 项目展开,结合我的理解,深入讲解其相关的知识点.技术原理.以及在实际开发过程中涉及到的部分代码 ...

  7. shell脚本中的逻辑判断

    shell脚本中也可以实现逻辑判断. 案例4:shell脚本中的逻辑判断 如果你学过C或者其他语言,相信你不会对if 陌生,在shell脚本中我们同样可以使用if逻辑判断.在shell中if判断的基本 ...

  8. Superset 筛选器理解

    免于被筛选器筛选,dashboard中,编辑,高级,"__time_range": {"scope": ["ROOT_ID"], " ...

  9. MySQL中联合主键的 in 查询

    就一句话: SELECT * from Projects WHERE (Prj,SubID) in (SELECT Prj,SubID FROM SP where stage='设计' and 负责人 ...

  10. oracle使用存储过程返回游标实现报表查询

    最近在oracle中通过存储过程实现一个报表查询,查询涉及到数据计算这里使用了临时表和存储过程实现输出查询,java接受游标变量返回结果集 第一步.创建统计使用的临时表 CREATE GLOBAL T ...