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

深入理解ConcurrentHashMap源码解析

ConcurrentHashMap是Java中一个非常重要的并发集合类,它提供了线程安全的哈希表实现。其初衷是为了优化同步HashMap,减少线程竞争,提高并发访问效率。随着Java的发展,ConcurrentHashMap在1.7和1.8中经历了显著的变化。以下内容将深入探索这两个版本的区别,同时结合源码和底层实现来进行说明。

1. Java 1.7中的ConcurrentHashMap

在Java 1.7(及之前的版本)中,ConcurrentHashMap采用了分段锁(Segmentation)的概念,其核心是将数据分成一段一段地存储,然后为每一段数据配备一把锁。

1.1 核心实现

在Java 1.7中,ConcurrentHashMap内部维护了一个Segment数组。每个Segment继承自ReentrantLock并且它内部本质上是一个Hash表。这样做的好处是能够减小锁的粒度,提高并发访问的效率。默认Segment 数量为 16,可以通过构造函数来修改默认值。当需要put或get一个元素时,线程首先通过hash定位到具体的Segment,然后在对应的Segment上进行锁定操作。

static final class Segment<K,V> extends ReentrantLock implements Serializable {// 省略其他属性和方法// Segment内部的HashEntry数组transient volatile HashEntry<K,V>[] table;Segment(float loadFactor, int threshold, HashEntry<K,V>[] tab) {this.loadFactor = loadFactor;this.threshold = threshold;this.table = tab;}// 其他方法...
}

1.2 写操作

当进行写操作时,需要首先定位到具体的Segment,然后对其加锁,执行操作后再解锁。这意味着同时只有一个线程可以在一个Segment内进行写操作,但是多个线程可以并发对不同的Segment进行操作。

public V put(K key, V value) {if (value == null)throw new NullPointerException();int hash = hash(key);int i = (hash >>> segmentShift) & segmentMask;return segments[i].put(key, hash, value, false);
}

1.3 读操作

对于读操作,如果没有进行结构修改,可以允许一定程度的并发。如果读操作需要确保最新的数据被读取,可能需要对Segment进行加锁。

2. Java 1.8中的ConcurrentHashMap

Java 8中对ConcurrentHashMap的实现进行了重大的改进。在这个版本中,去掉了Segment的概念,转而使用了CAS操作(Compare-And-Swap)和synchronized关键字配合节点的锁实现高效的并发控制。

2.1 核心实现

在Java 1.8中,ConcurrentHashMap内部主要是由Node数组构成,每个Node包含了一个key-value键值对。

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;volatile V value;volatile Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}// 其他方法...
}

2.2 写操作

写入时,如果目标位置为空(指的是 hash值 或数组上面的链表),将使用CAS操作进行写入。如果已经有节点存在,那么就使用synchronized锁定头节点,然后在链表上进行操作,如果是红黑树则在树上进行操作。

final V putVal(K key, V value, boolean onlyIfAbsent) {if (key == null || value == null) throw new NullPointerException();int hash = spread(key.hashCode());int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh; K fk; V fv;// ...if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {if (casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null)))break;                   // no lock when adding to empty bin}// ...}return null;
}

2.3 读操作

Java 8的ConcurrentHashMap在读操作上基本不加锁(除非在读操作过程中检测到写操作正在进行),利用volatile关键字的读写内存语义来保证可见性,从而大大提高读操作的并发性。

public V get(Object key) {Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;int h = spread(key.hashCode());if ((tab = table) != null && (n = tab.length) > 0 &&(e = tabAt(tab, (n - 1) & h)) != null) {if ((eh = e.hash) == h) {if ((ek = e.key) == key || (ek != null && key.equals(ek)))return e.val;}// ...}return null;
}

3. 结构优化

自Java 1.8开始,ConcurrentHashMap内部结构由链表逐渐转化为红黑树,以减少搜索时间。链表在元素数量增加到一定程度时会转换为红黑树结构。

4. 总结

Java 1.7的ConcurrentHashMap通过分段锁实现高并发,但它的并发度受限于Segment的数量。而Java 1.8通过精细化控制,只在必需时进行锁定,显著提升了读写性能,尤其是读操作几乎不受影响,这对于读多写少的场景来说是一个巨大的优化。

相关文章:

  • Azure Machine Learning - 使用 Azure OpenAI 服务生成文本
  • pytorch学习9-优化器学习
  • RepidJson将内容格式化后写入文件
  • 安卓adb【备忘录】
  • linux的权限741
  • uniapp-hubildx配置
  • 更改AndroidStudio模拟器位置
  • Linux系统调试课:PCIe调试手段
  • Verilog if语句阻断z状态传播
  • 【CSP】202309-2_坐标变换(其二)Python实现
  • 【python】pip install 国内源
  • PHP 判断给定两个时间是否在同一周,月,年
  • Android 手机屏幕适配方式和原理
  • javafx-在listview中添加了点击事件后会执行多次
  • C++ 预处理详解
  • [译]Python中的类属性与实例属性的区别
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 78. Subsets
  • android 一些 utils
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • C语言笔记(第一章:C语言编程)
  • download使用浅析
  • Elasticsearch 参考指南(升级前重新索引)
  • Odoo domain写法及运用
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • ReactNativeweexDeviceOne对比
  • spring cloud gateway 源码解析(4)跨域问题处理
  • 从零搭建Koa2 Server
  • 从零开始的无人驾驶 1
  • 讲清楚之javascript作用域
  • 跨域
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 使用agvtool更改app version/build
  • 首页查询功能的一次实现过程
  • 从如何停掉 Promise 链说起
  • #HarmonyOS:基础语法
  • (13)Hive调优——动态分区导致的小文件问题
  • (分布式缓存)Redis持久化
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (译)计算距离、方位和更多经纬度之间的点
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转)Sql Server 保留几位小数的两种做法
  • (转)大型网站的系统架构
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • * CIL library *(* CIL module *) : error LNK2005: _DllMain@12 already defined in mfcs120u.lib(dllmodu
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .htaccess配置重写url引擎
  • .NET Framework 4.6.2改进了WPF和安全性
  • .net FrameWork简介,数组,枚举
  • .NET简谈设计模式之(单件模式)
  • /var/log/cvslog 太大