【JDK 源码分析】ConcurrentHashMap 底层结构

简介: 【1月更文挑战第27天】【JDK 源码分析】ConcurrentHashMap 底层结构

ConcurrentHashMap针对JDK 1.7JDK 1.8版本上实现有不同。先来看看JDK 1.7版本的ConcurrentHashMap的底层实现。

JDK 1.7版本的ConcurrentHashMap底层维护了:Segments数组+HashEntry数组+链表,采用分段锁保证线程安全。

一个ConcurrentHashMap中有一个Segments数组,一个Segments中存储一个HashEntry数组,每个HashEntry是一个链表结构的元素。

segment继承自ReentrantLock锁。 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其他线程访问,实现了真正的并发访问。可以通过构造函数指定,数组扩容不会影响其他的segmentget无需加锁,volatile保证内存可见性。

JDK 1.8版本的ConcurrentHashMap底层维护了:Synchronized + CAS +Node + 红黑树。Nodevalnext都用volatile保证,保证可见性。查找,替换,赋值操作都使用CAS

1.1 内部属性:

先对ConcurrentHashMap中的几个重要属性进行了解:

// ConcurrentHashMap 最大容量:
private static final int MAXIMUM_CAPACITY = 1 << 30;
// ConcurrentHashMap 默认容量(和HashMap一致):
private static final int DEFAULT_CAPACITY = 16;
// ConcurrentHashMap 最大可能的数组大小:
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 默认的并发级别(不适用,为了兼容之前的版本):
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
// 默认的负载因子:
private static final float LOAD_FACTOR = 0.75f;
// 链表转红黑树阈值:
static final int TREEIFY_THRESHOLD = 8;
// 红黑树退化成链表的阈值:
static final int UNTREEIFY_THRESHOLD = 6;
// 红黑树化时,table数组最小值:
static final int MIN_TREEIFY_CAPACITY = 64;

除了这些定义的常量值,外有几个关键的内部属性:

  • tableNode数组:

HashMap不一样的地方是,ConcurrentHashMap中的table数组使用了volatile关键字进行修饰。

// 第一次新增元素时初始化,始终是2的幂:
transient volatile Node<K,V>[] table;

除了table数组外,还提供了nextTable数组:

// 扩容时用,代表扩容后的数组:
private transient volatile Node<K,V>[] nextTable;
  • Node节点的特殊hash值:
// 转移节点的hash值:
static final int MOVED     = -1; 
// (红黑)树根节点的hash值:
static final int TREEBIN   = -2; 
// 临时保留的hash值:
static final int RESERVED  = -3; 
// 普通节点hash的可用位:
static final int HASH_BITS = 0x7fffffff;
  • 控制table初始化和扩容的字段:
// -1 初始化中
// -n 表示n-1个线程正在扩容中
//  0 使用默认容量进行初始化
// >0 使用多少容量
private transient volatile int sizeCtl;

1.2 Node 节点:

这里我们重点关注的是Node中的两个属性valnext。和HashMap最大的区别就是,这两个属性使用了volatile关键字进行修饰,保证了这个两个变量的可见性以及有序性

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    volatile V val;
    volatile Node<K,V> next;
  // 构造器、操作方法......
}

关于volatile关键字的具体作用,需要参考一下JUC并发部分的文章,这里不过度解析。

相关文章
|
2月前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
35 1
|
4月前
|
算法 安全 Java
深入JDK源码:揭开ConcurrentHashMap底层结构的神秘面纱
【8月更文挑战第24天】`ConcurrentHashMap`是Java并发编程中不可或缺的线程安全哈希表实现。它通过精巧的锁机制和无锁算法显著提升了并发性能。本文首先介绍了早期版本中使用的“段”结构,每个段是一个带有独立锁的小型哈希表,能够减少线程间竞争并支持动态扩容以应对高并发场景。随后探讨了JDK 8的重大改进:取消段的概念,采用更细粒度的锁控制,并引入`Node`等内部类以及CAS操作,有效解决了哈希冲突并实现了高性能的并发访问。这些设计使得`ConcurrentHashMap`成为构建高效多线程应用的强大工具。
58 2
|
6月前
|
存储 算法 安全
JDK源码分析-HashMap
JDK源码分析-HashMap
|
7月前
|
存储 数据库
享元模式、在 JDK-Interger 的应用源码分析
享元模式(案例解析)、在 JDK-Interger 的应用源码分析
|
7月前
|
存储 网络协议 Java
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)
40 0
|
3月前
|
Java
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
378 3
|
4月前
|
Oracle Java 关系型数据库
Mac安装JDK1.8
Mac安装JDK1.8
770 4
|
4月前
|
Java 关系型数据库 MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【8月更文挑战第19天】在Linux上搭建Java Web应用环境,需安装JDK 1.8、Tomcat及MariaDB。本指南详述了使用apt-get安装OpenJDK 1.8的方法,并验证其版本。接着下载与解压Tomcat至`/usr/local/`目录,并启动服务。最后,通过apt-get安装MariaDB,设置基本安全配置。完成这些步骤后,即可验证各组件的状态,为部署Java Web应用打下基础。
64 1
|
1月前
|
Oracle Java 关系型数据库
安装 JDK 时应该注意哪些问题
选择合适的JDK版本需考虑项目需求与兼容性,推荐使用LTS版本如JDK 17或21。安装时注意操作系统适配,配置环境变量PATH和JAVA_HOME,确保合法使用许可证,并进行安装后测试以验证JDK功能正常。
51 1
|
1月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
66 1