大厂面试真题-ConcurrentHashMap怎么保证的线程安全?
ConcurrentHashMap是Java中的一个线程安全的哈希表实现,它通过一系列精妙的机制来保证线程安全。以下是ConcurrentHashMap保证线程安全的主要方式:
-
分段锁(Segment Locking,Java 1.8之前):
- 在Java 1.8之前的版本中,ConcurrentHashMap通过分段锁机制将整个哈希表分成多个段(Segment),每个段维护着一个独立的哈希表。
- 读操作时,并不需要获取整个哈希表的锁,而是只需要获取对应段的锁。这样可以降低并发度限制,允许多个线程同时读取不同的段,从而提高读操作的并发性能。
- 写操作时,需要锁定对应的段。虽然这仍然是一种锁机制,但相比于Hashtable等旧版哈希表的全局锁,分段锁能够显著提高并发性能。
-
CAS(Compare and Swap)操作和synchronized机制(Java 1.8及以后):
- Java 1.8版本的ConcurrentHashMap引入了新的数据结构和算法,进一步提高了并发性能。
- 在写操作中,ConcurrentHashMap使用CAS操作来保证更新的原子性。CAS操作是一种无锁的同步机制,它通过比较内存中的值与期望值,如果相等则进行更新,否则重试。这种机制避免了全局锁的开销,提高了写操作的并发性能。
- 同时,Java 1.8版本的ConcurrentHashMap也使用了synchronized机制来辅助实现线程安全,特别是在处理链表或红黑树等复杂数据结构时。
-
内部数据结构:
- ConcurrentHashMap使用了一种特殊的哈希表结构,即数组+链表+红黑树的结合体。
- 当链表长度超过一定阈值时,会将链表转换为红黑树,以提高查找、插入和删除操作的效率。这种数据结构的设计使得在单个段上的操作更加高效。
-
读写分离:
- ConcurrentHashMap允许多个线程同时进行读取操作,而写入操作会引起更细粒度的锁定。这样,多个读操作可以并发进行,提高了并发性能。
- 只有在写入操作时,才会对相关的段进行锁定,保证写入的原子性和一致性。
-
迭代器的线程安全性:
- ConcurrentHashMap的迭代器也是线程安全的。在迭代过程中,即使有其他线程对ConcurrentHashMap进行并发的修改操作,迭代器也不会抛出ConcurrentModificationException异常。
- 这是因为迭代器在迭代期间保持对ConcurrentHashMap的结构的快照,而不是直接操作ConcurrentHashMap。
综上所述,ConcurrentHashMap通过分段锁(Java 1.8之前)、CAS操作和synchronized机制(Java 1.8及以后)、特殊的内部数据结构、读写分离以及线程安全的迭代器等多种机制来保证线程安全。这些机制共同作用下,使得ConcurrentHashMap能够在多线程环境中提供高效的并发性能和数据一致性。
以下是一个使用ConcurrentHashMap
来保证线程安全的代码示例。这个例子将展示如何在多线程环境下安全地更新ConcurrentHashMap
中的数据。
假设我们需要统计一个字符串中各个单词出现的次数,并且这个统计过程需要在多线程环境中进行。我们可以使用ConcurrentHashMap
来存储单词及其出现的次数,并使用AtomicLong
来保证计数操作的原子性(尽管在Java 8及更高版本中,ConcurrentHashMap
的compute
方法已经足够高效和线程安全,但这里也提供一个使用AtomicLong
的示例作为对比)。
使用ConcurrentHashMap
和AtomicLong
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong; public class WordCountExample {
private final ConcurrentHashMap<String, AtomicLong> wordCounts = new ConcurrentHashMap<>(); public long increase(String word) {
AtomicLong count = wordCounts.computeIfAbsent(word, k -> new AtomicLong(0));
return count.incrementAndGet();
} public static void main(String[] args) throws InterruptedException {
WordCountExample example = new WordCountExample(); // 创建并启动多个线程来模拟并发增加单词计数
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
String word = "example"; // 这里以固定单词为例,实际应用中可以是不同的单词
example.increase(word);
}
}; Thread t1 = new Thread(task);
Thread t2 = new Thread(task); t1.start();
t2.start(); t1.join();
t2.join(); // 打印最终结果
System.out.println("Total count for 'example': " + example.wordCounts.get("example").get());
}
}
使用ConcurrentHashMap
的compute
方法
如果不使用AtomicLong
,而是直接使用ConcurrentHashMap
的compute
方法,也可以达到同样的效果,且代码更加简洁。
import java.util.concurrent.ConcurrentHashMap; public class WordCountExampleWithCompute {
private final ConcurrentHashMap<String, Long> wordCounts = new ConcurrentHashMap<>(); public long increase(String word) {
return wordCounts.compute(word, (k, v) -> v == null ? 1L : v + 1);
} public static void main(String[] args) throws InterruptedException {
// ...(与上面的main方法类似,只是实例化WordCountExampleWithCompute并调用其方法)
}
}
解释
- 使用
AtomicLong
:- 在
increase
方法中,我们首先尝试从ConcurrentHashMap
中获取与单词关联的AtomicLong
对象。 - 如果该单词不存在,则使用
computeIfAbsent
方法创建一个新的AtomicLong
对象,并将其初始化为0。 - 然后,使用
incrementAndGet
方法原子地增加计数器的值,并返回新的值。
- 在
- 使用
compute
方法:compute
方法接受一个键和一个重映射函数,该函数根据键和当前值(如果存在)计算新值。- 如果当前值为
null
,则函数返回1(表示这是第一次添加该单词)。 - 否则,函数返回当前值加1。
这两种方法都能在多线程环境中安全地更新ConcurrentHashMap
中的数据,但使用compute
方法的代码更加简洁。选择哪种方法取决于具体的应用场景和个人偏好。
相关文章:
大厂面试真题-ConcurrentHashMap怎么保证的线程安全?
ConcurrentHashMap是Java中的一个线程安全的哈希表实现,它通过一系列精妙的机制来保证线程安全。以下是ConcurrentHashMap保证线程安全的主要方式: 分段锁(Segment Locking,Java 1.8之前): 在Java 1.8之前的…...
【RabbitMQ】消息堆积、推拉模式
消息堆积 原因 消息堆积是指在消息队列中,待处理的消息数量超过了消费者处理能力,导致消息在队列中不断堆积的现象。通常有以下几种原因: 消息生产过快:在高流量或者高负载的情况下,生产者以极高的速率发送消息&…...
MySQL常用SQL语句(持续更新中)
文章目录 数据库相关表相关索引相关添加索引 编码相关系统变量相关 收录一些经常用到的sql 数据库相关 建数据库 CREATE DATABASE [IF NOT EXISTS] <数据库名> [[DEFAULT] CHARACTER SET <字符集名>] [[DEFAULT] COLLATE <校对规则名>];例如: C…...

【更新】红色文化之红色博物馆数据集(经纬度+地址)
数据简介:红色博物馆作为国家红色文化传承与爱国主义教育的重要基地,遍布全国各地,承载着丰富的革命历史与文化记忆。本数据说明旨在汇总并分析全国范围内具有代表性的红色博物馆的基本信息,包括其地址、特色及教育意义࿰…...

Python项目Flask框架整合Redis
一、在配置文件中创建Redis连接信息 二、 实现Redis配置类 import redis from config.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWD, REDIS_DB, EXPIRE_TIMEclass RedisDb():def __init__(self, REDIS_HOST, REDIS_PORT, REDIS_DB, EXPIRE_TIME, REDIS_PASSWD):# 建立…...

完整网络模型训练(一)
文章目录 一、网络模型的搭建二、网络模型正确性检验三、创建网络函数 一、网络模型的搭建 以CIFAR10数据集作为训练例子 准备数据集: #因为CIFAR10是属于PRL的数据集,所以需要转化成tensor数据集 train_data torchvision.datasets.CIFAR10(root&quo…...

高效便捷,体验不一样的韩语翻译神器
嘿,大家好啊!今天想跟大家聊聊我用过的几款翻译神器,特别是它们在翻译韩语时的那些小感受。作为一个偶尔需要啃啃韩语资料或者跟韩国朋友聊天的普通人,我真心觉得这些翻译工具简直就是我的救星! 一、福昕在线翻译 网址…...

Markdown笔记管理工具Haptic
什么是 Haptic ? Haptic 是一个新的本地优先、注重隐私的开源 Markdown 笔记管理工具。它简约、轻量、高效,旨在提供您所需的一切,而不包含多余的功能。 目前官方提供了 docker 和 Mac 客户端。 Haptic 仍在积极开发中。以下是未来计划的一些…...

网络原理-传输层UDP
上集回顾: 上一篇博客中讲述了应用层如何自定义协议:确定传输信息,确定数据格式 应用层也有一些现成的协议:HTTP协议 这一篇博客中来讲述传输层协议 传输层 socket api都是传输层协议提供的(操作系统内核实现的了…...

C++中,如何使你设计的迭代器被标准算法库所支持。
iterator(读写迭代器) const_iterator(只读迭代器) reverse_iterator(反向读写迭代器) const_reverse_iterator(反向只读迭代器) 以经常介绍的_DList类为例,它的迭代…...
Java NIO 全面详解:掌握 `Path` 和 `Files` 的一切
在 Java 7 中引入的 NIO (New I/O) 为文件系统和流的操作带来了强大的能力,其中 Path 和 Files 是核心部分。Path 作为对文件路径的抽象,提供了灵活的方式处理文件系统中的路径;Files 则通过一系列静态方法,使得文件的读写、复制、…...
bluez免提协议hands-free介绍,全到无法想象,bluez hfp ag介绍
零. 前言 由于Bluez的介绍文档有限,以及对Linux 系统/驱动概念、D-Bus 通信和蓝牙协议都有要求,加上网络上其实没有一个完整的介绍Bluez系列的文档,所以不管是蓝牙初学者还是蓝牙从业人员,都有不小的难度,学习曲线也相对较陡,所以我有了这个想法,专门对Bluez做一个系统…...

关于区块链的安全和隐私
背景 区块链技术在近年来发展迅速,被认为是安全计算的突破,但其安全和隐私问题在不同应用中的部署仍处于争论焦点。 目的 对区块链的安全和隐私进行全面综述,帮助读者深入了解区块链的相关概念、属性、技术和系统。 结构 首先介绍区块链…...

特征工程——一门提高机器学习性能的艺术
当前围绕人工智能(AI)和机器学习(ML)展开的许多讨论以模型为中心,聚焦于 ML和深度学习(DL)的最新进展。这种模型优先的方法往往对用于训练这些模型的数据关注不足,甚至完全忽视。类似MLOps的领域正迅速发展,通过系统性地训练和利用ML模型&…...

Paper解读:工作场所人机协作的团队形成:促进组织变革的目标编程模型
人工智能(AI)具有降低运营成本、提高效率和改善客户体验的潜力。 因此,在组织中组建项目团队至关重要,这样他们就会在决策过程中欢迎人工智能。 当前的技术革命要求公司快速变革,并增加了对团队在促进创新采用方面的作…...

图文深入理解Oracle Network配置管理(一)
List item 本篇图文深入介绍Oracle Network配置管理。 Oracle Network概述 Oracle Net 服务 Oracle Net 监听程序 <oracle_home>/network/admin/listener.ora <oracle_home>/network/admin/sqlnet.ora建立网络连接 要建立客户机或中间层连接,Oracle…...

leetcode-链表篇3
leetcode-61 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 示例 1: 输入:head [1,2,3,4,5], k 2 输出:[4,5,1,2,3]示例 2: 输入:head [0,1,2], k 4 输出&#x…...

RAG(Retrieval Augmented Generation)及衍生框架:CRAG、Self-RAG与HyDe的深入探讨
近年来,随着大型语言模型(LLMs)的迅猛发展,我们在寻求更精确、更可靠的语言生成能力上取得了显著进展。其中,检索增强生成(Retrieval-Augmented Generation)作为一种创新方法,极大地…...

C语言介绍
什么是C语言 C programing language 能干什么 Hello world? 如何学C语言 no reading no learning...

损失函数篇 | YOLOv10 更换损失函数之 MPDIoU | 《2023 一种用于高效准确的边界框回归的损失函数》
论文地址:https://arxiv.org/pdf/2307.07662v1.pdf 边界框回归(Bounding Box Regression,BBR)在目标检测和实例分割中得到了广泛应用,是目标定位的重要步骤。然而,对于边界框回归的大多数现有损失函数来说,当预测的边界框与真值边界框具有相同的长宽比,但宽度和高度的…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...

【Java多线程从青铜到王者】单例设计模式(八)
wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本,sleep也是可以指定时间的,也就是说时间一到就会解除阻塞,继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒),wait能被notify提前唤醒…...

C#中用于控制自定义特性(Attribute)
我们来详细解释一下 [AttributeUsage(AttributeTargets.Class, AllowMultiple false, Inherited false)] 这个 C# 属性。 在 C# 中,Attribute(特性)是一种用于向程序元素(如类、方法、属性等)添加元数据的机制。Attr…...