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

线程安全集合——ConcurrentHashMap

文章目录

  • 前言
  • 一、ConcurrentHashMap是什么?
  • 二、为什么需要ConcurrentHashMap?
    • 2.1 HashMap的问题
    • 2.2 Hashtable的局限性
    • 2.3 ConcurrentHashMap的优势
  • 三、如何使用ConcurrentHashMap
    • 3.1 基本用法
    • 3.2 常用方法
    • 3.3 多线程环境下的使用
  • 四、底层实现原理
    • 4.1 JDK 1.8之前:分段锁机制
    • 4.2 JDK 1.8及以后:CAS + synchronized
    • 4.3 核心方法实现概述
      • put() 方法
      • get() 方法

前言

在多线程开发中,HashMap虽然性能很好,但它不是线程安全的,在并发环境下可能会出现数据不一致甚至死循环的问题。虽然我们可以使用Hashtable或Collections.synchronizedMap()来获得线程安全的Map,但它们的性能在高并发场景下并不理想。这时候,ConcurrentHashMap就派上用场了。

一、ConcurrentHashMap是什么?

ConcurrentHashMap 是Java提供的一个线程安全的哈希表实现,它是Map接口的实现类。与Hashtable不同,ConcurrentHashMap采用了更加精细的锁机制,在保证线程安全的同时,大大提高了并发性能。

主要特点:

  • 线程安全,支持高并发读写
  • 性能优于Hashtable和Collections.synchronizedMap()
  • 不允许null键和null值
  • 迭代器具有弱一致性

二、为什么需要ConcurrentHashMap?

2.1 HashMap的问题

// HashMap在多线程环境下的问题演示
Map<String, Integer> map = new HashMap<>();// 多个线程同时操作可能导致数据不一致
new Thread(() -> {for (int i = 0; i < 1000; i++) {map.put("key" + i, i);}
}).start();new Thread(() -> {for (int i = 0; i < 1000; i++) {map.put("key" + i, i);}
}).start();

2.2 Hashtable的局限性

// Hashtable虽然线程安全,但性能较差
Hashtable<String, Integer> hashtable = new Hashtable<>();
// 每个方法都使用synchronized,并发性能不佳

2.3 ConcurrentHashMap的优势

// ConcurrentHashMap既保证线程安全,又有更好的性能
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 采用分段锁机制(JDK 1.8后改为CAS+synchronized)

三、如何使用ConcurrentHashMap

3.1 基本用法

public class ConcurrentHashMapExample {public static void main(String[] args) {// 创建ConcurrentHashMapConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 基本操作map.put("apple", 10);map.put("banana", 20);map.put("orange", 30);// 线程安全的获取操作Integer value = map.get("apple");System.out.println("apple的数量: " + value);// 线程安全的删除操作map.remove("banana");// 遍历操作map.forEach((key, val) -> {System.out.println(key + " -> " + val);});System.out.println("map大小: " + map.size());}
}

3.2 常用方法

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 1. 基本操作
map.put("key1", 100);
map.putIfAbsent("key2", 200);  // 如果key不存在才放入
Integer value = map.get("key1");
map.remove("key1");// 2. 原子操作方法
map.replace("key2", 200, 300);  // 原子性替换
map.compute("key3", (k, v) -> v == null ? 1 : v + 1);  // 原子性计算// 3. 批量操作
map.putAll(Map.of("key4", 400, "key5", 500));

3.3 多线程环境下的使用

public class ConcurrentTest {private static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();public static void main(String[] args) throws InterruptedException {// 创建多个线程同时操作Thread[] threads = new Thread[10];for (int i = 0; i < 10; i++) {final int threadId = i;threads[i] = new Thread(() -> {for (int j = 0; j < 1000; j++) {String key = "thread" + threadId + "_key" + j;map.put(key, j);}});threads[i].start();}// 等待所有线程完成for (Thread thread : threads) {thread.join();}System.out.println("最终map大小: " + map.size());}
}

四、底层实现原理

4.1 JDK 1.8之前:分段锁机制

在JDK 1.8之前,ConcurrentHashMap采用分段锁(Segment)机制:

  • 将整个哈希表分成多个段(Segment)
  • 每个段都有自己的锁
  • 不同段之间的操作可以并发进行

4.2 JDK 1.8及以后:CAS + synchronized

JDK 1.8对ConcurrentHashMap进行了重大改进:

  • 取消了分段锁机制
  • 采用CAS(Compare-And-Swap)+ synchronized的方式
  • 数据结构改为数组 + 链表 + 红黑树

4.3 核心方法实现概述

put() 方法

// 简化版的put方法逻辑
public V put(K key, V value) {// 1. 计算hash值int hash = hash(key);// 2. 根据hash值找到对应的桶Node<K,V>[] tab = table;int i = (tab.length - 1) & hash;// 3. 如果桶为空,使用CAS操作插入if (tab[i] == null) {// CAS操作,无锁插入casTabAt(tab, i, new Node<>(hash, key, value, null));} else {// 4. 如果桶不为空,使用synchronized锁住头节点synchronized (tab[i]) {// 在链表或红黑树中查找并插入}}
}

实现概述: 通过CAS操作处理空桶的插入,对于非空桶则使用synchronized锁住头节点,这样可以最大化并发性能。

get() 方法

// 简化版的get方法逻辑
public V get(Object key) {// 1. 计算hash值int hash = hash(key);// 2. 根据hash值找到对应的桶Node<K,V>[] tab = table;int i = (tab.length - 1) & hash;// 3. 无锁读取Node<K,V> e = tab[i];while (e != null) {if (e.hash == hash && key.equals(e.key)) {return e.val;}e = e.next;}return null;
}

实现概述: 读操作完全无锁,通过volatile保证数据的可见性,极大提高了读取性能。

http://www.lryc.cn/news/592028.html

相关文章:

  • 微店平台商品详情接口技术实现指南
  • 前端埋坑之js console.log字符换行后 html没换行问题处理
  • javascript 中数组对象操作方法
  • AE PDW2200电源射频手侧使用安装说明含电路图
  • 自学力扣:最长连续序列
  • CSS样式中的布局、字体、响应式布局
  • AI大模型打造金融智能信审助手04.七大金融监管相关政策
  • 【Oracle】centos7离线静默安装oracle11g(p13390677_112040)
  • 智象科技赋能金融、证券行业 IT 运维
  • PostgreSQL 16 Administration Cookbook 读书笔记:第7章 Database Administration
  • 【git仓库搭建笔记】
  • Oracle 19C 后台主要进程的功能解析
  • CertiK创始人顾荣辉出席上海Conflux大会,聚焦Web3全球化中的安全与合规路径
  • 赛思SLIC芯片、语音芯片原厂 赛思SLIC语音芯片ASX630:国产强“芯”赋能FTTR全光网络​
  • Docker Swarm 集群使用记录
  • 基于LiteNetLib的Server/Client Demo
  • 算法训练营day24 回溯算法③ 93.复原IP地址 、78.子集、 90.子集II
  • 零基础入门:用C++从零实现TCP Socket网络小工具
  • 人脸检测算法——SCRFD
  • Ubuntu系统下快速体验iperf3工具(网络性能测试)
  • 2G和3G网络关闭/退网状态(截止2025年7月)
  • Python技术题1
  • 【RK3576】【Android14】开发环境搭建
  • 基于现代R语言【Tidyverse、Tidymodel】的机器学习方法与案例分析
  • 用 React-Three-Fiber 实现雪花下落与堆积效果:从零开始的 3D 雪景模拟
  • 前端迟迟收不到响应,登录拦截器踩坑!
  • 小结:Spring MVC 的 XML 的经典配置方式
  • ASP.NET Core Web API 内存缓存(IMemoryCache)入门指南
  • untiy之导入插件(文件方式,适用于git克隆失败)
  • Instagram千号矩阵:亚矩阵云手机破解设备指纹检测的终极方案