讲师中心 微信公众号

工具推荐

视频效率加速
剧云
剧云

中文短剧剧本制作平台

updream

一键生成高质量推广成片

AniShort
AniShort

AI短剧协同制作平台

立刻MV
立刻MV

低成本歌曲 MV、视频生成

Java如何实现多线程安全集合?ConcurrentHashMap原理分析

夜静君_3802

夜静君_3802

发布时间:2025-07-10 17:41:02

|

384人浏览过

|

来源于php中文网

原创

在java中,实现线程安全集合的首选方式是使用java.util.concurrent包中的并发集合类。1. concurrenthashmap通过分段锁(jdk 7及之前)或cas+synchronized(jdk 8及以上)机制提供高并发性能,避免了全局锁带来的性能瓶颈;2. copyonwritearraylist适用于读多写少场景,通过写时复制保证线程安全;3. concurrentlinkedqueue和arrayblockingqueue分别适用于无界非阻塞和有界阻塞队列需求。相较于hashtable和collections.synchronizedmap的粗粒度锁机制,concurrenthashmap通过细粒度锁和无锁读操作显著提升了并发效率。其get方法利用volatile变量和不可变node对象实现无需加锁的线程安全读取。put操作则结合cas尝试无锁插入,并在冲突时锁定单个桶节点以支持并发修改。扩容时采用多线程协作迁移数据的方式,进一步减少了对整体性能的影响。因此,在并发编程中应优先选用这些专为并发优化的集合类。

Java如何实现多线程安全集合?ConcurrentHashMap原理分析

在Java中,要实现多线程安全的集合,最直接且高效的方式是使用java.util.concurrent包下提供的并发集合类。这些类经过精心设计,能够在高并发场景下提供优秀的性能和线程安全性,其中ConcurrentHashMap就是典范。当然,你也可以通过Collections.synchronizedMap()等方式对现有集合进行包装,但其并发性能通常不如专门的并发集合。

Java如何实现多线程安全集合?ConcurrentHashMap原理分析

解决方案

当我们需要一个线程安全的Map时,ConcurrentHashMap是首选。它通过精细的锁控制(而非全局锁)实现了高并发访问。对于列表,CopyOnWriteArrayList在读多写少场景下表现出色,因为它在写入时会复制底层数组,保证了读操作的无锁化。而对于队列,ConcurrentLinkedQueue和ArrayBlockingQueue等则提供了不同的并发队列实现,前者是无界非阻塞的,后者是有界阻塞的。选择哪种取决于具体的业务需求和并发模式。通常,优先考虑java.util.concurrent包中的类,它们在设计上就考虑到了并发问题,且性能经过了高度优化。

ConcurrentHashMap为什么比HashTable和Collections.synchronizedMap更高效?

这其实是个很有趣的问题,它揭示了并发编程中锁粒度对性能的决定性影响。HashTable和通过Collections.synchronizedMap包装的HashMap,它们实现线程安全的方式是简单粗暴的:在几乎所有公共方法上都加上了synchronized关键字。这意味着,无论你是在读数据还是写数据,只要有一个线程在操作这个Map,整个Map就被锁住了,其他所有试图访问的线程都得排队等待。这就像一家餐厅,只有一个服务员,每次只能服务一位顾客,效率自然低下。

立即学习“Java免费学习笔记(深入)”;

Java如何实现多线程安全集合?ConcurrentHashMap原理分析

ConcurrentHashMap则完全不同。在JDK 7及之前的版本,它采用了“分段锁”(Segment Lock)的机制,将整个Map划分为若干个Segment,每个Segment都是一个独立的HashTable。当一个线程修改某个Segment时,只会锁住这个Segment,其他线程仍然可以访问其他Segment。这就大大提升了并发度。

而到了JDK 8,ConcurrentHashMap的实现进一步优化,取消了Segment的概念,转而采用了“CAS(Compare-And-Swap)+ synchronized”的策略。它的核心思想是:

Java如何实现多线程安全集合?ConcurrentHashMap原理分析
  • 无锁读操作: get操作通常不需要加锁,因为它利用了volatile和内存屏障的特性,保证了读取到的数据是最新的。
  • CAS操作: 对于一些简单的修改操作,例如在桶(bucket)为空时尝试放入第一个节点,ConcurrentHashMap会尝试使用CAS操作来原子性地更新。如果成功,则无需加锁。
  • 细粒度锁: 只有当发生哈希冲突,需要修改链表或红黑树结构时,才会对链表或红黑树的头节点进行synchronized锁定。这意味着,即使在同一个桶内,只要不是修改同一个链表或树的结构,不同的线程也可以同时进行操作。当链表过长时,还会转换为红黑树以优化查找性能。

这种设计使得ConcurrentHashMap在大多数情况下都能实现很高的并发度,因为大部分操作都不需要等待全局锁,甚至不需要等待桶级别的锁,只有在真正需要修改共享结构时才加锁,而且锁的粒度非常小。

ImageMagick 批处理处理器
ImageMagick 批处理处理器

使用 ImageMagick 的 convert 和 mogrify 命令进行批量图像处理,支持调整大小、裁剪、水印和格式转换。与 libvips 集成以实现高性能缩略图生成。

下载

ConcurrentHashMap的get方法是如何实现线程安全的?

ConcurrentHashMap的get方法实现线程安全,但又无需加锁,这得益于Java内存模型(JMM)中的volatile关键字和其内部数据结构的巧妙设计。

ConcurrentHashMap的底层是一个Node数组,这个数组被volatile修饰。volatile确保了两点:

  1. 可见性: 当一个线程修改了Node数组中的某个元素(比如替换了一个Node对象),这个修改会立即对所有其他线程可见。
  2. 有序性: 编译器和处理器不会对volatile变量的操作进行重排序,保证了操作的顺序性。

在get操作中:

  • 它首先会读取volatile修饰的table数组,确保获取到的是最新的数组引用。
  • 接着,根据键的哈希值定位到对应的桶(Node数组的索引)。
  • 然后,它遍历该桶中的链表或红黑树来查找目标键值对。

这里的关键在于,Node对象本身(存储键值对)一旦被创建并放入桶中,其内部的键和值通常是不可变的(final修饰)。即使需要更新某个键的值,ConcurrentHashMap也可能通过创建新的Node并替换旧Node的方式来实现,或者利用CAS操作原子性地更新Node内部的值。由于get操作只是读取已经存在的Node及其内容,而volatile保证了Node数组的可见性,使得get总是能看到最新的Node引用。在遍历链表或红黑树时,由于Node之间的引用也是通过volatile或CAS保证了可见性和原子性,因此get操作可以在没有显式锁的情况下安全地进行。它避免了读写冲突,因为读操作不会阻塞写操作,写操作也不会阻塞读操作(除非写操作正在改变整个桶的结构,比如链表转红黑树,但即便如此,get也只是读取旧的引用,最终会看到最新的状态)。

ConcurrentHashMap在put操作中如何处理并发冲突和扩容?

ConcurrentHashMap的put操作是其并发性能的核心体现,它巧妙地结合了CAS和synchronized来处理并发冲突和扩容。

处理并发冲突: 当一个线程调用put方法时:

  1. 计算哈希值: 首先根据键计算哈希值,并确定目标桶(Node数组的索引)。
  2. 检查桶状态:
    • 如果目标桶为空,put操作会尝试使用CAS来放置第一个Node。如果CAS成功,操作完成,无需加锁。
    • 如果目标桶不为空,说明该桶已经有元素。此时,put操作会尝试对该桶的头节点进行synchronized锁定。这个锁是针对单个桶的,而不是整个Map。
  3. 遍历与插入/更新:
    • 在获取到桶锁后,线程会遍历链表或红黑树。
    • 如果找到相同的键,则更新其值。这可能涉及CAS操作(如果Node支持原子更新)或在锁内直接修改。
    • 如果未找到相同键,则将新的Node插入到链表末尾或红黑树中。
  4. 链表转红黑树: 如果某个桶的链表长度超过了TREEIFY_THRESHOLD(默认为8),ConcurrentHashMap会将该链表转换为红黑树,以优化查找性能。这个转换过程也是在桶锁的保护下进行的。

通过这种方式,ConcurrentHashMap避免了对整个Map的锁定,允许多个线程同时修改不同桶中的元素,大大提高了并发度。只有当多个线程恰好要修改同一个桶时,才需要竞争同一个桶锁。

处理扩容(Resizing):ConcurrentHashMap的扩容机制也设计得非常巧妙,支持并发扩容:

  1. 触发扩容: 当Map中的元素数量达到一定阈值(capacity * loadFactor)时,会触发扩容。
  2. 创建新表: ConcurrentHashMap会创建一个两倍于当前容量的新Node数组。
  3. 并发迁移: 扩容并不是由一个线程独自完成的,而是可以由多个线程协助完成。
    • 当一个线程发现Map正在扩容时(通过检查桶中是否有ForwardingNode标记),它会加入到迁移工作中。
    • 每个线程负责迁移一部分桶。它会从旧表的末尾开始,向头部遍历,每次处理一个或多个桶。
    • 在迁移每个桶时,该线程会锁定当前桶的头节点,然后将该桶中的所有Node重新哈希并复制到新表中对应的位置。
    • 迁移完成后,旧表中的对应桶会被放置一个ForwardingNode,表示该桶的数据已经迁移到新表,并指向新表。
  4. CAS更新表引用: 当所有桶都迁移完成后,ConcurrentHashMap会使用CAS操作原子性地将table引用指向新的Node数组。

这种并发迁移的设计,使得扩容过程不会长时间阻塞整个Map的读写操作,进一步提升了ConcurrentHashMap在高并发场景下的可用性和性能。它巧妙地利用了CAS和细粒度锁,将一个看似复杂的全局操作分解为多个可以并行执行的小任务。

热门AI工具

更多
蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

函子Zion

一款支持AI搭建、可视化编辑和多端发布的全栈无代码开发平台。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

854

2025.10.23

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2921

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

416

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

437

2026.01.06

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

512

2026.05.09

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4858

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

1369

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

463

2026.01.21

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

40

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
DBeaver驱动管理手册
DBeaver驱动管理手册

共0课时 | 0人学习

夸克AI浏览器使用手册
夸克AI浏览器使用手册

共0课时 | 0人学习

jQuery官方API文档
jQuery官方API文档

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn