java集合详解 - ConcurrentHashMap详解

ConcurrentHashMap 是 Java 并发包 (java.util.concurrent) 中提供的一个线程安全的哈希表实现,它是对 HashMap 的线程安全版本,但比 Hashtable 或 Collections.synchronizedMap() 有更好的并发性能。

目录

 基本特性

Java 7 实现 (分段锁)

Java 8 实现 (CAS + synchronized)

重要参数

Java 8 中的并发控制

扩容机制

与其它Map的比较

常见问题

核心源码解析

设计亮点解析

核心数据结构 

哈希表定义

节点类型 

        基础节点(链表)

树节点(红黑树) 

并发控制关键代码 

原子访问方法 

putVal核心逻辑 

扩容关键代码

transfer 方法(核心扩容逻辑)

ForwardingNode 设计 

并发计数实现 

addCount 方法 


 基本特性

  • 线程安全:支持多线程并发访问而不需要外部同步

  • 高并发:通过分段锁或 CAS 操作实现高并发性能

  • 弱一致性:迭代器反映的是创建时的状态,不保证反映所有更新

  • 不允许 null 键/值:与 HashMap 不同,不允许 null 键或值

Java 7 实现 (分段锁)

在 Java 7 中,ConcurrentHashMap 使用分段锁技术:

  • 将整个哈希表分成多个段 (Segment),每个段是一个独立的哈希表

  • 每个段有自己的锁,不同段可以并发操作

  • 默认创建 16 个段,并发级别为 16

Java 8 实现 (CAS + synchronized)

Java 8 对 ConcurrentHashMap 进行了重大改进:

  • 取消了分段锁设计

  • 使用 CAS (Compare-And-Swap) + synchronized 实现更细粒度的锁

  • 当发生哈希冲突时,只锁住冲突的链表或红黑树的头节点

  • 引入红黑树处理哈希冲突,当链表长度超过阈值(默认为8)时转换为红黑树

重要参数

  • initialCapacity:初始容量,默认16

  • loadFactor:负载因子,默认0.75f

  • concurrencyLevel:并发级别(Java7),Java8中仅用于兼容性

  • TREEIFY_THRESHOLD:链表转红黑树的阈值,默认为8

  • UNTREEIFY_THRESHOLD:红黑树转链表的阈值,默认为6

Java 8 中的并发控制

  1. CAS:用于无竞争情况下的快速操作

  2. synchronized:当发生哈希冲突时,锁住链表或树的头节点

  3. volatile:保证变量的可见性

  4. sizeCtl:控制表初始化和扩容的特殊标志

扩容机制

  • 当元素数量超过容量*负载因子时触发扩容

  • 扩容时创建新表(大小为原表2倍)

  • 采用多线程协助扩容机制,提高扩容效率

  • 扩容期间可以同时进行查询操作

与其它Map的比较

特性HashMapHashtableConcurrentHashMap
线程安全
锁粒度无锁全表锁分段锁/桶锁
允许null键/值
迭代器弱一致性
性能最高最低

常见问题

为什么使用 synchronized 而不是 ReentrantLock?

  • 性能考量:Java 8优化后的synchronized在单bucket竞争时性能更好

  • 内存占用:synchronized的JVM内置锁更节省内存

  • JIT优化:锁消除/锁膨胀等优化更成熟

如何保证扩容期间的一致性?

  • ForwardingNode机制:已迁移的bucket会被标记,读操作自动跳转新表

  • 写操作协作:遇到ForwardingNode的写入线程会先协助扩容

size()为何不精确?

  • 性能权衡:精确计数需要全局锁,采用分片计数(baseCount + CounterCell[])牺牲精确性换取并发度

核心源码解析

设计亮点解析

  1. 锁粒度优化:仅锁单个bucket头节点(Java 7锁Segment)

  2. 无锁读路径:所有读操作都不加锁,依赖volatile和final保证可见性

  3. 协作式扩容:多线程共同完成数据迁移

  4. 状态机设计:通过MOVED、TREEBIN等特殊hash值实现状态转换

  5. 缓存友好:使用@Contended避免伪共享

核心数据结构 

  • 哈希表定义
transient volatile Node<K,V>[] table;  // 主哈希表,volatile保证可见性
private transient volatile int sizeCtl; // 控制表初始化和扩容
  • 节点类型 
        基础节点(链表)
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;       // 不可变hash值
    final K key;          // 不可变key
    volatile V val;       // volatile保证可见性
    volatile Node<K,V> next; // volatile保证可见性
    
    Node(int hash, K key, V val, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.val = val;
        this.next = next;
    }
}
树节点(红黑树) 
static final class TreeNode<K,V> extends Node<K,V> {
    TreeNode<K,V> parent;  // 红黑树父节点
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // 链表前驱(用于删除)
    boolean red;
    
    // 树查找方法
    final TreeNode<K,V> find(int h, Object k) {
        // 红黑树查找实现...
    }
}

并发控制关键代码 

原子访问方法 
// Unsafe原子操作
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
    return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
}

static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                    Node<K,V> c, Node<K,V> v) {
    return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
putVal核心逻辑 
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // 1. 计算hash(二次哈希)
    int hash = spread(key.hashCode());
    int binCount = 0;
    
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        
        // 2. 表未初始化则初始化
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        
        // 3. 目标bucket为空则CAS插入
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<>(hash, key, value, null)))
                break;
        }
        
        // 4. 检测到扩容则协助扩容
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        
        // 5. bucket非空则同步处理
        else {
            synchronized (f) {  // 锁住bucket头节点
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {  // 链表处理
                        // ...遍历链表...
                        if (binCount >= TREEIFY_THRESHOLD)
                            treeifyBin(tab, i);  // 链表转树
                    }
                    else if (f instanceof TreeBin) {  // 树处理
                        // ...红黑树插入...
                    }
                }
            }
        }
    }
    // 6. 更新size
    addCount(1L, binCount);
    return null;
}

扩容关键代码

transfer 方法(核心扩容逻辑)
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
    int n = tab.length, stride;
    
    // 1. 计算每个线程处理的bucket范围(最小16)
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE;
    
    // 2. 初始化新表(2倍容量)
    if (nextTab == null) {
        try {
            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
            nextTab = nt;
        } catch (Throwable ex) { ... }
        nextTable = nextTab;
        transferIndex = n;  // 从高位开始迁移
    }
    
    // 3. 迁移主循环
    while (advance) {
        // 分配任务区间(原子更新transferIndex)
        if (--i >= bound || finishing)
            advance = false;
        else if ((nextIndex = transferIndex) <= 0) {
            i = -1;
            advance = false;
        }
        else if (U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex,
              nextBound = (nextIndex > stride ? nextIndex - stride : 0))) {
            bound = nextBound;
            i = nextIndex - 1;
            advance = false;
        }
    }
    
    // 4. 处理单个bucket
    if (i < 0 || i >= n || i + n >= nextn) {
        // 完成检查...
    }
    else if ((f = tabAt(tab, i)) == null)
        advance = casTabAt(tab, i, null, fwd);  // 标记为空bucket
    else if ((fh = f.hash) == MOVED)
        advance = true;  // 已处理
    else {
        synchronized (f) {  // 锁住bucket头节点
            // 链表/树拆分逻辑...
            if (f instanceof TreeBin) {
                TreeBin<K,V> t = (TreeBin<K,V>)f;
                // 红黑树拆分...
            }
        }
    }
}
ForwardingNode 设计 
static final class ForwardingNode<K,V> extends Node<K,V> {
    final Node<K,V>[] nextTable;
    
    ForwardingNode(Node<K,V>[] tab) {
        super(MOVED, null, null, null);  // hash=MOVED(-1)
        this.nextTable = tab;
    }
    
    Node<K,V> find(int h, Object k) {
        // 转发到新表查询
        outer: for (Node<K,V>[] tab = nextTable;;) {
            Node<K,V> e; int n;
            if (k == null || tab == null || (n = tab.length) == 0)
                return null;
            if ((e = tabAt(tab, (n - 1) & h)) == null)
                return null;
            for (;;) {
                int eh; K ek;
                if ((eh = e.hash) == h &&
                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                    return e;
                if (eh < 0) {
                    if (e instanceof ForwardingNode) {
                        tab = ((ForwardingNode<K,V>)e).nextTable;
                        continue outer;
                    }
                    else
                        return e.find(h, k);
                }
                if ((e = e.next) == null)
                    return null;
            }
        }
    }
}

并发计数实现 

addCount 方法 
private final void addCount(long x, int check) {
    CounterCell[] cs; long b, s;
    
    // 1. 尝试直接更新baseCount
    if ((cs = counterCells) != null ||
        !U.compareAndSetLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        
        // 2. 使用CounterCell分散热点
        CounterCell c; long v; int m;
        boolean uncontended = true;
        if (cs == null || (m = cs.length - 1) < 0 ||
            (c = cs[ThreadLocalRandom.getProbe() & m]) == null ||
            !(uncontended = U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))) {
            
            fullAddCount(x, uncontended);  // 完整计数逻辑
            return;
        }
        if (check <= 1)
            return;
        s = sumCount();
    }
    
    // 3. 检查扩容
    if (check >= 0) {
        while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
               (n = tab.length) < MAXIMUM_CAPACITY) {
            // 触发扩容...
        }
    }
}

HashMap详解  HashLink详解 ArrayList详解 CopyOnWriteArrayList详解 HashSet详解 LinkedHashMap详解 LinkedHashSet详解 TreeSet详解 TreeMap详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值