当前位置: 首页 > 知识库问答 >
问题:

Java 8 ConcurrentHashMap

汪飞捷
2023-03-14

我观察到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是一个表的包装:< code> transient volatile节点

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

共有3个答案

宋畅
2023-03-14

Java语言规范写道:

如果我们有两个动作x和y,我们写hb(x, y)来表示x发生在y之前。

>

  • 如果x和y是同一线程的动作,并且x在程序顺序上位于y之前,则hb(x,y)。

    从对象的构造函数的结尾到该对象的终结器(12.6)的开头有一个发生前边缘。

    如果一个动作 x 与下一个动作 y 同步,那么我们也有 hb(x, y)。

    如果 hb(x, y) 和 hb(y, z),则 hb(x, z)。

    并定义

    同步操作导致操作上的同步关系,定义如下:

    >

  • 监视器m上的解锁操作与m上的所有后续锁定操作同步(其中“后续”是根据同步顺序定义的)。

    对易失性变量v(§8.3.1.4)的写入与任何线程对v的所有后续读取同步(其中“后续”是根据同步顺序定义的)。

    启动线程的操作与它启动的线程中的第一个操作同步。

    向每个变量写入默认值(零、假或空)与每个线程中的第一个动作同步。

    虽然在分配包含变量的对象之前向变量写入默认值看起来有点奇怪,但从概念上讲,每个对象都是在程序开始时用其默认的初始值创建的。

    线程T1中的最终动作与检测到T1已终止的另一线程T2中的任何动作同步。

    T2可以通过调用T1.isAlive()或T1.join()来实现这一点。

    如果线程T1中断线程T2,则T1的中断与任何其他线程(包括T2)确定T2已中断的任何点同步(通过引发InterruptedException或调用thread.interrupted或thread.isIInterrupted)。

    也就是说,读取易失性字段的建立与显式锁一样发生在之前。

  • 公良高刚
    2023-03-14

    该文档没有说明发生了同步。例如,它指出

    [...]聚合操作,如< code>putAll和< code>clear,并发检索可能只反映某些条目的插入或删除。

    换句话说,允许并发使用和提供同步访问之间存在差异。

    高砚
    2023-03-14

    节点#val易失性,它在订购前建立您的发生。

    synchronized不是线程安全的要求,它是工具箱中使系统线程安全的一个工具。您必须考虑这个ConcurrentHashMap上的一整套操作,以了解线程安全性。

    知道原始的Con

    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#值volatile。这是线程安全的同步点。

    CHM-8的< code>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;
    

    因此,在这种情况下,一个非空值应该确保与put之前的操作相关的发生前关系。

     类似资料:

    相关问答

    相关文章

    相关阅读