下面是Java7中ConcurrentHashMap
的示例:
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K,V> next;
HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
所以如果一个线程看到next
字段,它保证value
字段不是null
吗?
:
因此,如果我在测试中约束value
在初始化后不能修改,并且null
值不能由构造器分配,那么采用next
获得HashEntry
的线程将永远不会将value
视为null
?
在给出的构造函数代码中,对value
的赋值总是被视为发生在对next
的赋值之前。但是,在对并发代码中的不变量进行推理时,必须非常小心。可能有其他线程并发地执行分配给value
的其他代码。由于您的问题已经假设实例被发布到其他线程,因此几乎不可能确保没有其他编写器。
最后,请注意,在给定代码的情况下,value
实际上可能一开始就被分配为null
。
引自Atomic Access:
使用易失性变量降低了内存一致性错误的风险,因为任何对易失性变量的写入与后续对相同变量的读取建立了happens-before关系。这意味着对volatile变量的更改对于其他线程总是可见的。更重要的是,这也意味着当一个线程读取一个易失性变量时,它不仅看到对易失性变量的最新更改,还看到导致更改的代码的副作用。
可以看到,happens-before relationship
是针对相同的变量。
谢谢@JBNizet的提醒。
next
的"副作用"可以是分配给value
的代码。但它只是意味着线程将看到value
,不保证其他线程不会将value
设置为其他值,包括null
。
考虑如何调用构造函数:
myHashEntry = new HashEntry(hash, key, value, next);
直到构造函数运行完成后才设置myHashEntry。在构造函数仍在运行时,没有其他可用的对象引用。因此,其他线程无法并发地查看部分构造的对象。