JUC并发编程 并发HashMap实现机制

32 阅读11分钟

并发HashMap实现机制

1. 概述

ConcurrentHashMap是Java并发包中提供的线程安全的哈希表实现,它在保证线程安全的同时,提供了比Hashtable更好的并发性能。本文将深入分析其实现机制,包括数据结构、核心算法和并发控制策略。

2. 简单使用例子

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        // 创建ConcurrentHashMap实例
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        
        // 创建线程池模拟并发操作
        ExecutorService executor = Executors.newFixedThreadPool(4);
        
        // 并发添加元素
        for (int i = 0; i < 100; i++) {
            final int value = i;
            executor.submit(() -> {
                map.put("key" + value, value);
                System.out.println("Added: key" + value + " = " + value);
            });
        }
        
        // 并发读取元素
        for (int i = 0; i < 50; i++) {
            final int value = i;
            executor.submit(() -> {
                Integer result = map.get("key" + value);
                System.out.println("Read: key" + value + " = " + result);
            });
        }
        
        // 原子操作示例
        map.putIfAbsent("atomicKey", 100);
        map.replace("key1", 1, 101);
        map.computeIfAbsent("computeKey", k -> k.length());
        
        executor.shutdown();
    }
}

3. 核心接口定义

3.1 ConcurrentMap接口

ConcurrentMap接口继承自Map接口,提供了线程安全的原子操作方法:

public interface ConcurrentMap<K,V> extends Map<K,V> {
    // 原子性的条件插入操作
    V putIfAbsent(K key, V value);
    
    // 原子性的条件删除操作
    boolean remove(Object key, Object value);
    
    // 原子性的条件替换操作
    boolean replace(K key, V oldValue, V newValue);
    
    // 原子性的替换操作
    V replace(K key, V value);
    
    // 计算型操作
    default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction);
    default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
    default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
    default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction);
}

4. 内存结构分析

4.1 核心数据结构

ConcurrentHashMap的内存结构主要包括以下组件:

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> 
    implements ConcurrentMap<K,V>, Serializable {
    
    // 哈希表数组,存储Node节点
    transient volatile Node<K,V>[] table;
    
    // 扩容时的新表
    private transient volatile Node<K,V>[] nextTable;
    
    // 元素计数基础值
    private transient volatile long baseCount;
    
    // 控制表初始化和扩容的字段
    // 负数表示正在初始化或扩容:-1表示初始化,-(1+扩容线程数)表示扩容
    // 正数表示下次扩容的阈值
    private transient volatile int sizeCtl;
    
    // 扩容时的转移索引
    private transient volatile int transferIndex;
    
    // 计数器数组,用于减少竞争
    private transient volatile CounterCell[] counterCells;
}

4.2 Node节点结构

// 基本节点类型
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;      // 哈希值
    final K key;         // 键
    volatile V val;      // 值(volatile保证可见性)
    volatile Node<K,V> next;  // 下一个节点(链表结构)
    
    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 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;
    }
}

// 红黑树节点
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;
}

4.3 内存布局图

graph TB
    subgraph "ConcurrentHashMap内存结构"
        CHM["ConcurrentHashMap"]
        Table["table: Node[]"]
        NextTable["nextTable: Node[]"]
        SizeCtl["sizeCtl: int"]
        BaseCount["baseCount: long"]
        CounterCells["counterCells: CounterCell[]"]
        
        CHM --> Table
        CHM --> NextTable
        CHM --> SizeCtl
        CHM --> BaseCount
        CHM --> CounterCells
        
        subgraph "桶结构"
            Bucket0["bucket[0]"]
            Bucket1["bucket[1]"]
            BucketN["bucket[n]"]
            
            Table --> Bucket0
            Table --> Bucket1
            Table --> BucketN
        end
        
        subgraph "链表结构"
            Node1["Node1"]
            Node2["Node2"]
            Node3["Node3"]
            
            Bucket0 --> Node1
            Node1 --> Node2
            Node2 --> Node3
        end
        
        subgraph "红黑树结构"
            TreeBin["TreeBin"]
            TreeNode1["TreeNode"]
            TreeNode2["TreeNode"]
            TreeNode3["TreeNode"]
            
            Bucket1 --> TreeBin
            TreeBin --> TreeNode1
            TreeNode1 --> TreeNode2
            TreeNode1 --> TreeNode3
        end
    end

5. 核心源码分析

5.1 初始化机制

/**
 * 初始化哈希表
 * 使用sizeCtl控制并发初始化,确保只有一个线程能够初始化表
 */
private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    // 循环直到表被成功初始化
    while ((tab = table) == null || tab.length == 0) {
        // 如果sizeCtl < 0,说明其他线程正在初始化,当前线程让出CPU
        if ((sc = sizeCtl) < 0)
            Thread.yield(); // 让出CPU时间片
        // 尝试CAS设置sizeCtl为-1,表示当前线程正在初始化
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                // 双重检查,确保表仍未初始化
                if ((tab = table) == null || tab.length == 0) {
                    // 确定初始容量
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                    @SuppressWarnings("unchecked")
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                    table = tab = nt;
                    // 设置下次扩容阈值为容量的0.75倍
                    sc = n - (n >>> 2);
                }
            } finally {
                // 恢复sizeCtl为扩容阈值
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}

5.2 添加元素机制

/**
 * 添加键值对的核心实现
 * @param key 键
 * @param value 值
 * @param onlyIfAbsent 如果为true,不替换已存在的值
 */
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // ConcurrentHashMap不允许null键或null值
    if (key == null || value == null) throw new NullPointerException();
    
    // 计算哈希值,使用spread方法确保哈希值为正数
    int hash = spread(key.hashCode());
    int binCount = 0;  // 记录桶中节点数量
    
    // 无限循环,直到成功插入
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        
        // 如果表未初始化,先初始化表
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        // 如果目标桶为空,使用CAS直接插入新节点
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
                break;  // 插入成功,跳出循环
        }
        // 如果遇到转发节点,说明正在扩容,协助扩容
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            // 锁定桶的第一个节点,进行同步操作
            synchronized (f) {
                // 双重检查,确保f仍是桶的第一个节点
                if (tabAt(tab, i) == f) {
                    // 处理链表结构
                    if (fh >= 0) {
                        binCount = 1;
                        // 遍历链表
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            // 找到相同的键,更新值
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            // 到达链表末尾,插入新节点
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key, value, null);
                                break;
                            }
                        }
                    }
                    // 处理红黑树结构
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        // 在红黑树中插入节点
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            
            // 检查是否需要树化
            if (binCount != 0) {
                // 如果链表长度达到树化阈值,转换为红黑树
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    
    // 增加元素计数,检查是否需要扩容
    addCount(1L, binCount);
    return null;
}

5.3 查找元素机制

/**
 * 获取指定键对应的值
 * @param key 要查找的键
 * @return 对应的值,如果不存在则返回null
 */
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;
        }
        // 如果是特殊节点(转发节点或树根节点)
        else if (eh < 0)
            return (p = e.find(h, key)) != null ? p.val : null;
        
        // 遍历链表查找
        while ((e = e.next) != null) {
            if (e.hash == h &&
                ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}

5.4 扩容机制

/**
 * 扩容操作的核心实现
 * 将元素从旧表迁移到新表,支持多线程协作
 */
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
    int n = tab.length, stride;
    
    // 计算每个线程处理的桶数量,最少16个
    if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
        stride = MIN_TRANSFER_STRIDE;
    
    // 如果新表为空,创建新表(容量翻倍)
    if (nextTab == null) {
        try {
            @SuppressWarnings("unchecked")
            Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
            nextTab = nt;
        } catch (Throwable ex) {
            sizeCtl = Integer.MAX_VALUE;
            return;
        }
        nextTable = nextTab;
        transferIndex = n;  // 设置转移索引
    }
    
    int nextn = nextTab.length;
    // 创建转发节点,标记已迁移的桶
    ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
    boolean advance = true;  // 是否继续处理下一个桶
    boolean finishing = false;  // 是否完成扩容
    
    // 主循环:处理每个桶的迁移
    for (int i = 0, bound = 0;;) {
        Node<K,V> f; int fh;
        
        // 获取下一个需要处理的桶索引
        while (advance) {
            int nextIndex, nextBound;
            if (--i >= bound || finishing)
                advance = false;
            else if ((nextIndex = transferIndex) <= 0) {
                i = -1;
                advance = false;
            }
            // CAS更新transferIndex,获取处理范围
            else if (U.compareAndSwapInt
                     (this, TRANSFERINDEX, nextIndex,
                      nextBound = (nextIndex > stride ?
                                   nextIndex - stride : 0))) {
                bound = nextBound;
                i = nextIndex - 1;
                advance = false;
            }
        }
        
        // 检查是否完成所有桶的迁移
        if (i < 0 || i >= n || i + n >= nextn) {
            int sc;
            if (finishing) {
                // 扩容完成,更新table引用
                nextTable = null;
                table = nextTab;
                sizeCtl = (n << 1) - (n >>> 1);  // 设置新的扩容阈值
                return;
            }
            // 减少扩容线程计数
            if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                    return;
                finishing = advance = true;
                i = n; // 重新检查
            }
            continue;
        }
        
        // 处理空桶
        if ((f = tabAt(tab, i)) == null)
            advance = casTabAt(tab, i, null, fwd);
        // 桶已被其他线程处理
        else if ((fh = f.hash) == MOVED)
            advance = true;
        else {
            // 锁定桶,进行迁移
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    Node<K,V> ln, hn;  // 低位链表和高位链表
                    
                    // 处理普通链表节点
                    if (fh >= 0) {
                        int runBit = fh & n;  // 计算在新表中的位置
                        Node<K,V> lastRun = f;
                        
                        // 找到最后一段连续的相同runBit的节点
                        for (Node<K,V> p = f.next; p != null; p = p.next) {
                            int b = p.hash & n;
                            if (b != runBit) {
                                runBit = b;
                                lastRun = p;
                            }
                        }
                        
                        // 设置低位或高位链表的起始节点
                        if (runBit == 0) {
                            ln = lastRun;
                            hn = null;
                        } else {
                            hn = lastRun;
                            ln = null;
                        }
                        
                        // 构建低位和高位链表
                        for (Node<K,V> p = f; p != lastRun; p = p.next) {
                            int ph = p.hash; K pk = p.key; V pv = p.val;
                            if ((ph & n) == 0)
                                ln = new Node<K,V>(ph, pk, pv, ln);
                            else
                                hn = new Node<K,V>(ph, pk, pv, hn);
                        }
                        
                        // 将链表放入新表的对应位置
                        setTabAt(nextTab, i, ln);          // 低位:原位置
                        setTabAt(nextTab, i + n, hn);      // 高位:原位置+n
                        setTabAt(tab, i, fwd);             // 标记原桶已迁移
                        advance = true;
                    }
                    // 处理红黑树节点
                    else if (f instanceof TreeBin) {
                        TreeBin<K,V> t = (TreeBin<K,V>)f;
                        TreeNode<K,V> lo = null, loTail = null;
                        TreeNode<K,V> hi = null, hiTail = null;
                        int lc = 0, hc = 0;
                        
                        // 遍历红黑树,分离为两个链表
                        for (Node<K,V> e = t.first; e != null; e = e.next) {
                            int h = e.hash;
                            TreeNode<K,V> p = new TreeNode<K,V>
                                (h, e.key, e.val, null, null);
                            if ((h & n) == 0) {
                                if ((p.prev = loTail) == null)
                                    lo = p;
                                else
                                    loTail.next = p;
                                loTail = p;
                                ++lc;
                            } else {
                                if ((p.prev = hiTail) == null)
                                    hi = p;
                                else
                                    hiTail.next = p;
                                hiTail = p;
                                ++hc;
                            }
                        }
                        
                        // 根据节点数量决定是否保持树结构
                        ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                            (hc != 0) ? new TreeBin<K,V>(lo) : t;
                        hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                            (lc != 0) ? new TreeBin<K,V>(hi) : t;
                        
                        // 将结果放入新表
                        setTabAt(nextTab, i, ln);
                        setTabAt(nextTab, i + n, hn);
                        setTabAt(tab, i, fwd);
                        advance = true;
                    }
                }
            }
        }
    }
}

6. 模拟数据和操作流程

6.1 初始化流程

sequenceDiagram
    participant T1 as Thread1
    participant T2 as Thread2
    participant CHM as ConcurrentHashMap
    participant Table as table[]
    
    T1->>CHM: new ConcurrentHashMap()
    CHM->>CHM: sizeCtl = DEFAULT_CAPACITY
    CHM->>CHM: table = null
    
    T1->>CHM: put("key1", "value1")
    CHM->>CHM: table == null, 需要初始化
    CHM->>CHM: CAS(sizeCtl, 16, -1) 成功
    CHM->>Table: 创建 Node[16]
    CHM->>CHM: sizeCtl = 12 (0.75 * 16)
    CHM->>Table: table = 新数组
    
    T2->>CHM: put("key2", "value2")
    CHM->>CHM: table != null, 直接使用

6.2 添加元素流程

假设我们有一个初始容量为4的ConcurrentHashMap,演示添加元素的过程:

初始状态
graph LR
    subgraph "ConcurrentHashMap初始状态"
        Table["table[4]"]
        B0["bucket[0]: null"]
        B1["bucket[1]: null"]
        B2["bucket[2]: null"]
        B3["bucket[3]: null"]
        
        Table --> B0
        Table --> B1
        Table --> B2
        Table --> B3
    end
    
    SizeCtl["sizeCtl: 3"]
    BaseCount["baseCount: 0"]
添加第一个元素 put("key1", "value1")
// 假设 "key1".hashCode() = 1001
// spread(1001) = 1001 & 0x7fffffff = 1001
// 桶索引 = 1001 & (4-1) = 1001 & 3 = 1
graph LR
    subgraph "添加key1后的状态"
        Table["table[4]"]
        B0["bucket[0]: null"]
        B1["bucket[1]"]
        B2["bucket[2]: null"]
        B3["bucket[3]: null"]
        
        Node1["Node<br/>hash:1001<br/>key:'key1'<br/>val:'value1'<br/>next:null"]
        
        Table --> B0
        Table --> B1
        Table --> B2
        Table --> B3
        B1 --> Node1
    end
    
    SizeCtl["sizeCtl: 3"]
    BaseCount["baseCount: 1"]
添加第二个元素 put("key2", "value2") - 无冲突
// 假设 "key2".hashCode() = 2002
// spread(2002) = 2002
// 桶索引 = 2002 & 3 = 2
graph LR
    subgraph "添加key2后的状态"
        Table["table[4]"]
        B0["bucket[0]: null"]
        B1["bucket[1]"]
        B2["bucket[2]"]
        B3["bucket[3]: null"]
        
        Node1["Node<br/>hash:1001<br/>key:'key1'<br/>val:'value1'<br/>next:null"]
        Node2["Node<br/>hash:2002<br/>key:'key2'<br/>val:'value2'<br/>next:null"]
        
        Table --> B0
        Table --> B1
        Table --> B2
        Table --> B3
        B1 --> Node1
        B2 --> Node2
    end
    
    SizeCtl["sizeCtl: 3"]
    BaseCount["baseCount: 2"]
添加第三个元素 put("key3", "value3") - 发生冲突
// 假设 "key3".hashCode() = 1005
// spread(1005) = 1005
// 桶索引 = 1005 & 3 = 1 (与key1冲突)
graph LR
    subgraph "添加key3后的状态(链表)"
        Table["table[4]"]
        B0["bucket[0]: null"]
        B1["bucket[1]"]
        B2["bucket[2]"]
        B3["bucket[3]: null"]
        
        Node1["Node<br/>hash:1001<br/>key:'key1'<br/>val:'value1'<br/>next:→"]
        Node3["Node<br/>hash:1005<br/>key:'key3'<br/>val:'value3'<br/>next:null"]
        Node2["Node<br/>hash:2002<br/>key:'key2'<br/>val:'value2'<br/>next:null"]
        
        Table --> B0
        Table --> B1
        Table --> B2
        Table --> B3
        B1 --> Node1
        Node1 --> Node3
        B2 --> Node2
    end
    
    SizeCtl["sizeCtl: 3"]
    BaseCount["baseCount: 3"]
添加第四个元素触发扩容
// 当前元素数量(3) >= sizeCtl(3),触发扩容
// 新容量 = 4 * 2 = 8
// 新的sizeCtl = 8 * 0.75 = 6
graph TB
    subgraph "扩容过程"
        OldTable["旧表 table[4]"]
        NewTable["新表 nextTable[8]"]
        
        subgraph "旧表内容"
            OB0["[0]: null"]
            OB1["[1]: key1→key3"]
            OB2["[2]: key2"]
            OB3["[3]: null"]
        end
        
        subgraph "新表内容"
            NB0["[0]: null"]
            NB1["[1]: key1"]
            NB2["[2]: key2"]
            NB3["[3]: null"]
            NB4["[4]: null"]
            NB5["[5]: key3"]
            NB6["[6]: null"]
            NB7["[7]: null"]
        end
        
        OldTable --> OB0
        OldTable --> OB1
        OldTable --> OB2
        OldTable --> OB3
        
        NewTable --> NB0
        NewTable --> NB1
        NewTable --> NB2
        NewTable --> NB3
        NewTable --> NB4
        NewTable --> NB5
        NewTable --> NB6
        NewTable --> NB7
        
        OB1 -."迁移".-> NB1
        OB1 -."迁移".-> NB5
        OB2 -."迁移".-> NB2
    end

6.3 删除元素流程

删除操作 remove("key3")
sequenceDiagram
    participant Client as 客户端
    participant CHM as ConcurrentHashMap
    participant Bucket as bucket[5]
    participant Node as Node链表
    
    Client->>CHM: remove("key3")
    CHM->>CHM: 计算hash = spread("key3".hashCode())
    CHM->>CHM: 计算索引 i = hash & (length-1) = 5
    CHM->>Bucket: 获取bucket[5]的头节点
    CHM->>Bucket: synchronized(头节点)
    CHM->>Node: 遍历链表查找key3
    Node->>CHM: 找到匹配节点
    CHM->>Node: 从链表中移除节点
    CHM->>CHM: 更新计数器 baseCount--
    CHM->>Client: 返回被删除的值

6.4 查找元素流程

查找操作 get("key1")
sequenceDiagram
    participant Client as 客户端
    participant CHM as ConcurrentHashMap
    participant Table as table[]
    participant Node as Node
    
    Client->>CHM: get("key1")
    CHM->>CHM: 计算hash = spread("key1".hashCode())
    CHM->>CHM: 计算索引 i = hash & (length-1)
    CHM->>Table: 获取table[i]的头节点
    
    alt 头节点就是目标
        Table->>CHM: 头节点key匹配
        CHM->>Client: 返回头节点的值
    else 需要遍历链表
        CHM->>Node: 遍历next指针
        Node->>CHM: 找到匹配的key
        CHM->>Client: 返回节点的值
    else 红黑树查找
        CHM->>Node: 调用TreeBin.find()
        Node->>CHM: 在红黑树中查找
        CHM->>Client: 返回找到的值
    end

7. 并发控制机制

7.1 分段锁策略

ConcurrentHashMap使用桶级别的锁,而不是整个表的锁:

// 锁定桶的第一个节点
synchronized (f) {  // f是桶的头节点
    // 在这个同步块中,只有当前桶被锁定
    // 其他桶的操作不受影响
    if (tabAt(tab, i) == f) {  // 双重检查
        // 执行插入、删除或更新操作
    }
}

7.2 CAS无锁操作

// 原子性地设置数组元素
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                    Node<K,V> c, Node<K,V> v) {
    return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}

// 原子性地获取数组元素
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int index) {
    return (Node<K,V>)U.getObjectVolatile(tab, ((long)index << ASHIFT) + ABASE);
}

7.3 计数器机制

/**
 * 计数器单元,用于减少计数竞争
 */
@sun.misc.Contended static final class CounterCell {
    volatile long value;  // 计数值
    CounterCell(long x) { value = x; }
}

/**
 * 增加计数的实现
 * 优先使用baseCount,发生竞争时使用CounterCell数组
 */
private final void addCount(long x, int check) {
    CounterCell[] as; long b, s;
    // 尝试直接更新baseCount
    if ((as = counterCells) != null ||
        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        
        CounterCell a; long v; int m;
        boolean uncontended = true;
        
        // 如果baseCount更新失败,使用CounterCell
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
            !(uncontended =
              U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
            // 如果CounterCell也竞争激烈,进入fullAddCount
            fullAddCount(x, uncontended);
            return;
        }
        if (check <= 1)
            return;
        s = sumCount();  // 重新计算总数
    }
    
    // 检查是否需要扩容
    if (check >= 0) {
        Node<K,V>[] tab, nt; int n, sc;
        while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
               (n = tab.length) < MAXIMUM_CAPACITY) {
            int rs = resizeStamp(n);
            if (sc < 0) {
                // 正在扩容,尝试协助
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                    transferIndex <= 0)
                    break;
                // 增加扩容线程计数
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                    transfer(tab, nt);
            }
            // 开始扩容
            else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                         (rs << RESIZE_STAMP_SHIFT) + 2))
                transfer(tab, null);
            s = sumCount();
        }
    }
}

8. 性能优化特性

8.1 红黑树优化

当链表长度超过8且表容量大于64时,链表会转换为红黑树:

/**
 * 将链表转换为红黑树
 */
private final void treeifyBin(Node<K,V>[] tab, int index) {
    Node<K,V> b; int n, sc;
    if (tab != null) {
        // 如果表容量小于64,优先扩容而不是树化
        if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
            tryPresize(n << 1);
        else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
            synchronized (b) {
                if (tabAt(tab, index) == b) {
                    TreeNode<K,V> hd = null, tl = null;
                    // 将链表节点转换为树节点
                    for (Node<K,V> e = b; e != null; e = e.next) {
                        TreeNode<K,V> p =
                            new TreeNode<K,V>(e.hash, e.key, e.val,
                                              null, null);
                        if ((p.prev = tl) == null)
                            hd = p;
                        else
                            tl.next = p;
                        tl = p;
                    }
                    // 创建TreeBin并设置到桶中
                    setTabAt(tab, index, new TreeBin<K,V>(hd));
                }
            }
        }
    }
}

8.2 协助扩容机制

多个线程可以协助进行扩容操作,提高扩容效率:

/**
 * 协助扩容
 */
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
    Node<K,V>[] nextTab; int sc;
    if (tab != null && (f instanceof ForwardingNode) &&
        (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
        int rs = resizeStamp(tab.length);
        while (nextTab == nextTable && table == tab &&
               (sc = sizeCtl) < 0) {
            if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                sc == rs + MAX_RESIZERS || transferIndex <= 0)
                break;
            // 增加协助线程计数
            if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                transfer(tab, nextTab);
                break;
            }
        }
        return nextTab;
    }
    return table;
}

9. 总结

ConcurrentHashMap通过以下关键技术实现了高效的并发控制:

  1. 分段锁机制:使用桶级别的锁,减少锁竞争
  2. CAS无锁操作:在无竞争情况下避免加锁开销
  3. 协助扩容:多线程协作进行扩容,提高扩容效率
  4. 红黑树优化:长链表转换为红黑树,优化查找性能
  5. 分离计数器:使用CounterCell数组减少计数竞争
  6. 内存可见性:使用volatile关键字保证内存可见性

这些设计使得ConcurrentHashMap在保证线程安全的同时,提供了优秀的并发性能,是Java并发编程中的重要工具。

关键性能指标

  • 读操作:几乎无锁,性能接近HashMap
  • 写操作:只锁定相关桶,并发度高
  • 扩容操作:支持多线程协助,扩容效率高
  • 内存开销:相比Hashtable更低,相比HashMap略高

通过深入理解ConcurrentHashMap的实现原理,我们可以更好地在并发场景中使用这个强大的数据结构。