首页 > 编程语言 >【揭秘】ConcurrentHashMap的神秘面纱:源码深度解读,让你成为并发编程高手!

【揭秘】ConcurrentHashMap的神秘面纱:源码深度解读,让你成为并发编程高手!

时间:2024-11-28 16:58:49浏览次数:7  
标签:Node ConcurrentHashMap tab ek int 源码 key null 揭秘

ConcurrentHashMap 是 Java 中一个线程安全的哈希表实现,它允许多个线程并发地读取和写入映射。与 Hashtable 不同,ConcurrentHashMap 使用了一种分段锁(Segment Lock)机制来提高并发性能。

以下是 ConcurrentHashMap 的源码解读和详解:

1. 类定义和成员变量

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable {
    private static final long serialVersionUID = 7249069246763182397L;

    // 默认初始容量
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    // 最大容量
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    // 默认负载因子
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 并发级别,即段的数量
    static final int DEFAULT_CONCURRENCY_LEVEL = 16;

    // 用于存储节点的数组
    transient volatile Node<K, V>[] table;

    // 访问计数器,用于触发重散列操作
    private transient volatile long baseCount;

    // 其他成员变量...
}

2. 构造函数

ConcurrentHashMap 提供了多个构造函数,可以指定初始容量、负载因子和并发级别。

public ConcurrentHashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}

public ConcurrentHashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}

public ConcurrentHashMap(int initialCapacity, float loadFactor) {
    this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
}

public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
    // 初始化代码...
}

3. 核心方法

3.1 put 方法

put 方法是插入或更新键值对的核心方法。

public V put(K key, V value) {
    return putVal(key, value, false);
}

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;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else 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
        } else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (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;
}
3.2 get 方法

get 方法是获取键对应的值的核心方法。

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;
}

4. 辅助方法

4.1 spread 方法

spread 方法用于将哈希码的高16位与低16位进行异或运算,以减少哈希冲突。

static final int spread(int h) {
    return (h ^ (h >>> 16)) & HASH_BITS;
}
4.2 initTable 方法

initTable 方法用于初始化哈希表。

private final Node<K, V>[] initTable() {
    Node<K, V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        if ((sc = sizeCtl) < 0)
            Thread.yield(); // lost initialization race; just spin
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                if ((tab = table) == null || tab.length == 0) {
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                    Node<K, V>[] nt = (Node<K, V>[]) new Node<?, ?>[n];
                    table = tab = nt;
                    sc = n - (n >>> 2);
                }
            } finally {
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}

5. 总结

ConcurrentHashMap 通过分段锁机制和高效的哈希算法,实现了高并发环境下的高效读写操作。其核心在于通过细粒度的锁控制和无锁读操作,提高了并发性能。同时,通过重散列和树化等机制,保证了在高并发情况下的性能稳定性。

标签:Node,ConcurrentHashMap,tab,ek,int,源码,key,null,揭秘
From: https://blog.csdn.net/Q2024107/article/details/144089194

相关文章