Java 8 ConcurrentHashMap

2022-09-02 04:29:11

我观察到 ConcurrentHashMap 在 Java 8 中完全重写,更加“无锁”。我已经浏览了该方法的代码,发现没有显式锁定机制: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;
}

问题:

如何从一个线程中看到其他线程对此哈希映射所做的修改,因为代码不在同步保护伞下(这将强制实施发生之前的关系)?

注意:整个 ConcurrentHashMap 是表的包装器: transient volatile Node<K,V>[] table;

因此,table 是对数组的易失性引用,而不是对易失性元素数组的引用!这意味着,如果有人正在更新此数组中的元素,则在其他线程中不会看到修改。


答案 1

简短的回答

是它建立你的发生之前订购。Node#valvolatile

更长的答案

synchronized不是线程安全的要求,它是工具箱中的一个工具,用于使系统线程安全。您必须考虑对此的一整套操作,以推理线程安全性。ConcurrentHashMap

知道原件也是非阻塞的,这很有用。Java 8 CHM 获取之前的通知ConcurrentHashMap

V get(Object key, int hash) {
    if (count != 0) { // read-volatile
        HashEntry<K,V> e = getFirst(hash);
        while (e != null) {
            if (e.hash == hash && key.equals(e.key)) {
                V v = e.value;
                if (v != null)
                    return v;
                return readValueUnderLock(e); // ignore this
            }
            e = e.next;
        }
    }
    return null;
}

在这种情况下,没有阻塞,那么它是如何工作的呢?是 .这是线程安全的同步点。HashEntry#valuevolatile

CHM-8的类是相同的。Node

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    volatile V val;
    volatile Node<K,V> next;

因此,在这种情况下,非空值应确保在看跌期权之前的行为方面,存在之前的关系。


答案 2

文档未说明发生同步。例如,它声明

[...]聚合操作(如 和 )并发检索可能仅反映插入或删除某些条目。putAllclear

换句话说,允许并发使用和提供同步访问之间是有区别的。


推荐