首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java 8 ConcurrentHashMap

Java 8 ConcurrentHashMap
EN

Stack Overflow用户
提问于 2017-06-09 14:35:37
回答 3查看 3.6K关注 0票数 21

我注意到,ConcurrentHashMap已经在Java 8中被完全重写为更“无锁”。我浏览了get()方法的代码,发现没有显式的锁机制:

代码语言:javascript
复制
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;
}

问题:

如何从一个线程中查看如何从其他线程对此hashmap进行修改,因为代码不是在同步伞下(这将强制执行happens-before 关系)?

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

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

EN

回答 3

Stack Overflow用户

发布于 2017-06-09 14:47:11

简短回答

Node#valvolatile,它确定您在订货前发生的情况。

较长答案

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

知道原始的ConcurrentHashMap也是非阻塞的是有用的。注意之前的Java 8 CHM get

代码语言:javascript
复制
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类是相同的.

代码语言:javascript
复制
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    volatile V val;
    volatile Node<K,V> next;

因此,在这种情况下,一个非空的val应该确保你发生-之前的关系有关的行动之前,一个空头。

票数 14
EN

Stack Overflow用户

发布于 2017-06-09 14:44:34

文档没有声明同步发生。例如,它声明

..。聚合操作(如putAllclear )、并发检索可能只反映某些条目的插入或删除。

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

票数 5
EN

Stack Overflow用户

发布于 2017-06-11 17:30:18

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中的最终操作与另一个线程T2中检测到T1已终止的任何操作同步。 T2可以通过调用T1.isAlive()或T1.join()来实现这一点。
  • 如果线程T1中断线程T2,则T1的中断将同步--与任何其他线程(包括T2)确定T2已被中断(通过引发InterruptedException或调用Thread.interrupted或Thread.isInterrupted)的任何点同步。

也就是说,读取易失性字段就会发生--就像显式锁一样。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44460503

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档