当前位置: 首页 > news >正文

ConcurrentHashMap

ConcurrentHashMap 是 Java 中用于在多线程环境下高效存储和操作键值对的哈希表实现,下面从基本概念、底层数据结构、线程安全机制、关键方法、性能特点以及使用场景几个方面详细介绍:

基本概念

ConcurrentHashMapjava.util.concurrent 包下的一个类,它继承自 AbstractMap 类并实现了 ConcurrentMap 接口。与 HashMap 不同,ConcurrentHashMap 是线程安全的,在多线程环境下可以高效地进行并发操作,避免了 HashMap 在多线程环境下可能出现的数据不一致和线程安全问题。

底层数据结构

JDK 1.7
  • 分段锁机制ConcurrentHashMap 使用分段锁(Segment)来实现线程安全。整个哈希表被分成多个 Segment,每个 Segment 类似于一个小的 HashMap,它继承自 ReentrantLock,可以独立加锁。不同的 Segment 可以被不同的线程同时访问,从而提高并发性能。
  • 数据结构:每个 Segment 内部包含一个哈希表,由数组和链表组成。
JDK 1.8
  • CAS + synchronized:摒弃了分段锁机制,采用 CAS(Compare-And-Swap,比较并交换)和 synchronized 来保证线程安全。
  • 数据结构:底层数据结构是数组 + 链表 + 红黑树。当链表长度超过 8 且数组长度大于 64 时,链表会转换为红黑树,以提高查找效率。

线程安全机制

JDK 1.7
  • 每个 Segment 都有自己独立的锁,不同的 Segment 可以并行操作。当一个线程访问某个 Segment 时,会对该 Segment 加锁,其他线程可以同时访问其他 Segment,从而提高并发度。
JDK 1.8
  • CAS 操作:在插入元素时,首先会使用 CAS 操作尝试更新数组中的节点,如果 CAS 操作成功,则插入成功;如果失败,则说明有其他线程在同时操作,会进入下一步处理。
  • synchronized 锁:当发生哈希冲突时,会使用 synchronized 对链表或红黑树的头节点加锁,保证同一时刻只有一个线程可以对该链表或红黑树进行操作。

关键方法

put(K key, V value)
  • 作用:将指定的键值对插入到 ConcurrentHashMap 中。
  • 实现:在 JDK 1.8 中,首先计算键的哈希值,找到对应的桶位置。如果桶为空,使用 CAS 操作将新节点插入;如果桶不为空,对桶的头节点加锁,遍历链表或红黑树,若键已存在则更新值,否则插入新节点。
get(Object key)
  • 作用:根据指定的键获取对应的值。
  • 实现:计算键的哈希值,找到对应的桶位置,然后遍历链表或红黑树查找键对应的值。由于 get 操作不需要加锁,所以性能较高。
size()
  • 作用:返回 ConcurrentHashMap 中键值对的数量。
  • 实现:在 JDK 1.8 中,size 方法会先尝试使用无锁的方式统计元素数量,如果失败则会加锁统计。

性能特点

  • 高并发性能:由于采用了分段锁(JDK 1.7)或 CAS + synchronized(JDK 1.8)的机制,ConcurrentHashMap 可以支持多个线程同时进行读写操作,并发性能较高。
  • 空间开销:相比于 HashMapConcurrentHashMap 为了保证线程安全,会有一定的空间开销,例如分段锁(JDK 1.7)和额外的 CAS 操作。

使用场景

  • 多线程环境下的缓存:在多线程环境中,如果需要使用缓存来存储数据,ConcurrentHashMap 是一个不错的选择,它可以保证线程安全,同时具有较高的并发性能。
  • 共享数据存储:当多个线程需要共享一个哈希表时,使用 ConcurrentHashMap 可以避免数据不一致和线程安全问题。

示例代码

import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 插入键值对map.put("apple", 1);map.put("banana", 2);map.put("cherry", 3);// 获取值Integer value = map.get("banana");System.out.println("Value of banana: " + value);// 遍历键值对for (ConcurrentHashMap.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());}// 删除键值对map.remove("cherry");System.out.println("After removing cherry, size: " + map.size());}
}

通过上述代码可以看到,ConcurrentHashMap 的使用方式与 HashMap 类似,但它在多线程环境下可以保证线程安全。


http://www.mrgr.cn/news/92847.html

相关文章:

  • 初始化列表
  • 详解:事务注解 @Transactional
  • 安装 Open WebUI
  • 【HarmonyOS NEXT】控制 WebP 格式动图播放次数的实现方案
  • Redis---缓存穿透,雪崩,击穿
  • MySQL慢查询分析与处理
  • C++:类和对象(下篇)
  • 实验环境搭建集锦(docker linux ros2+强化学习环境+linux上单片机串口调试)
  • nuxt常用组件库html-validator、@nuxtjs/i18n、@nuxt/image、@unocss/nuxt使用解析
  • dj算法与分层图最短路径
  • LeetCode 解题思路 6(Hot 100)
  • 机器学习:线性回归,梯度下降,多元线性回归
  • 入门基础项目(SpringBoot+Vue)
  • AI辅助学习vue第十四章
  • 进程的状态 ─── linux第11课
  • C++22——哈希
  • Cybellum的使用场景开始之一
  • “深入浅出”系列之C++:(32)“流”的本质
  • Python从0到100(八十九):Resnet、LSTM、Shufflenet、CNN四种网络分析及对比
  • 关于流水线的理解