ConcurrentHashMap介绍

ConcurrentHashMap

JDK1.7 版本到 JDK1.8 版本的演变

JDK1.7 版本

采用的是分段锁,每个segment都是独立的,可以并发访问不同的segment,默认是16个segment,所以最多是16个线程并发执行

JDK1.8 版本

移除了segment,锁的粒度变得更加细粒化,锁只在红黑树和链表的节点级别进行,通过CAS进行插入操作,如果某个节点为空,就通过CAS插入节点,如果不为空,就使用sychronized锁定冲突的节点的头结点,进一步减少锁的竞争,提高并发度

为什么ConcurrentHashMap不允许Key,Value为空

为了避免混淆和潜在的开发问题

ConcurrentHashMap是为了并发环境而设计的,在并发访问的时候,如果key-value为 null,可能会发生歧义,比如,在使用get()时,返回值为null,无法确定是 key 为 null 还是 value 为 null。

为了简化实现

如果key或者value允许为 null,会给put, get, containsKey带来一些操作上面的复杂性,例如get操作可能频繁返回null,那就需要额外的逻辑去判断究竟是key为 null 还是 value为 null

如果允许为 null,那就会出现下面的情况

  1. 是不是因为 value 本身就是 null
  2. 是不是 key 不存在
  3. 是不是另一个线程正在更新

调用者很难去判断这些情况,而且还可能导致:

  1. 重复计算
  2. 数据不一致
  3. 并发错误

ConcurrentHashMap 的 get 方法是否需要加锁

不需要加锁

通过volatileConcurrentHashMap能够确保get方法的线程安全,即使在写入发生时,读取仍然能够获取最新的数据,不会引发并发的问题

ConcurrentHashMap 部分源码:

static class Node<K,V> implements Map.Entry<K,V> {
        volatile V val;
        volatile Node<K,V> next;
		// ...CODE...
}

具体是通过Unsafe#getXXXXVolatile和利用volatile来修饰节点的 val 和 next 指针来实现的

static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
    return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
 public native Object getObjectVolatile(Object o, long offset);

Unsafe

Unsafe是 Java 提供的内部类,用来执行不安全操作,并提供了直接操作内存和现成的能力

常见的关键功能:

  • 直接内存访问:允许直接分配、释放、读写内存
  • 对象操作:允许直接操作对象的字段,如获取和设置对象的字段值
  • 线程操作:包含暂停和恢复线程、管理锁等
  • CAS 操作:提供了compare-and-swap操作,支持原子性更新操作

与HashMap,HashTable比较

Map 类型 是否允许 null 原因
HashMap 允许 单线程下没有歧义
Hashtable 不允许 历史原因 + 避免歧义
ConcurrentHashMap 不允许 多线程下 null 会导致逻辑不确定
posted @ 2025-11-20 18:32  Lantz12  阅读(0)  评论(0)    收藏  举报