在数据竞争期间,线程是否可以读取volatile变量的初始空值?特别是在构造函数中为它赋给非null值时?

时间:2010-12-03 07:03:41

标签: java concurrency volatile memory-model

  • 让我感到困惑的是这个。

ConcurrentHashMap中的HashEntry Java文档(jdk1.6.0_16)

...因为值字段是易失性的,而不是最终的,所以对于非同步读取器而言,Java内存模型在通过数据争用读取时看到null而不是初始值是合法的。尽管实际上不太可能发生重新排序,但是如果在非同步访问方法中看到null(预初始化)值,则Segment.readValueUnderLock方法将用作备份。

  • 这里是ConcurrentHashMap#Segment

    的get方法的实现
    
    V get(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry 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); // recheck
                    }
                    e = e.next;
                }
            }
            return null;
        }
    
  • 和readValueUnderLock


V readValueUnderLock(HashEntry e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }
  • 根据我的阅读和理解,每个线程都会读取volatile变量的最新值。

  • 那么线程何时会读取初始空值?特别是在HashEntry中,在构造函数完成之前赋值。 (另请注意,HashEntry的引用永远不会转义其构造函数。)

  • 我很难过,有人可以在ConcurrentHashMap(jdk1.6.0_16)中解释上面的HashEntry java doc。为什么需要额外的预防措施锁定?

2 个答案:

答案 0 :(得分:3)

当Java 1.5发布时,JMM中有一条规定可以部分初始化HashEntry。也就是说,当线程放入地图时,会创建HashEntry并将其指定为对桶头或collison成员的引用。此时,条目的值可能尚未分配给其他线程。

CHM假设如果条目不为null,则该值不应为null,因此readValueUnderLock作为故障保护放入。

我向DL询问了这个确切的情况,他说虽然有可能发生这种情况,但它永远不应该。他还说,自1.6以来,这个问题不会发生。

答案 1 :(得分:0)

在构造函数完成之前,您必须确保任何人都无法使用对Map的引用。当它是一个私有字段并且只能通过getter方法访问时应该很容易做到 - 包括在该字段所在的类中。

构造函数将在实例getter方法被调用之前完成。