Java 两个“;易挥发;构造函数中的变量在关系之前发生?

Java 两个“;易挥发;构造函数中的变量在关系之前发生?,java,concurrency,java.util.concurrent,concurrenthashmap,concurrent-programming,Java,Concurrency,Java.util.concurrent,Concurrenthashmap,Concurrent Programming,下面是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; th

下面是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;
}
静态最终类HashEntry{
最终整数散列;
最终K键;
挥发性V值;
下一个条目;
HashEntry(int散列,K键,V值,下一个HashEntry){
this.hash=hash;
this.key=key;
这个值=值;
this.next=next;
}
因此,如果线程看到
next
字段,是否可以保证
字段不是
null


更新


因此,如果我限制在我的测试中,
在初始化后不能修改,并且构造函数不能分配
null
值,那么使用
下一步
获取
HashEntry
的线程将永远不会在呈现的构造函数代码中看到
作为
null

赋值给
value
总是被认为是在分配给
next
之前发生的。但是,在对并发代码中的不变量进行推理时必须非常小心。可能有其他线程同时执行分配给
value
的其他代码。因为您的问题已经假设实例是publi如果将其转移到其他线程,则几乎不可能确保没有其他写入程序

最后,请注意,
实际上可能在一开始就被赋值为
null
,因为给出了代码。

引用自(突出部分是我的):

使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与该同一变量的后续读取建立“发生在之前”的关系。这意味着对易失性变量的更改总是对其他线程可见。此外,这还意味着当线程读取volatile变量时,它不仅可以看到volatile的最新更改,还可以看到导致更改的代码的副作用

如您所见,
发生在关系
用于同一变量之前

谢谢@JBNizet的提醒


next
的“副作用”可以是分配给
value
的代码。但这只是意味着线程将看到
而不是保证其他线程不会将
设置为其他值,包括
null

考虑如何调用构造函数:

myHashEntry = new HashEntry(hash, key, value, next);

myHashEntry在构造函数运行到完成之前不会设置。当构造函数仍在运行时,没有其他对该对象的引用可用。因此,其他线程无法同时查看部分构造的对象内部。

volatile不会将null转换为非null。它保证值可见对于其他线程,无论该值是否为null。广义而言,线程安全包括两个方面:可见性和原子性。
volatile
声明说明了可见性,但不能保证原子性(通常通过互斥提供)。在状态转换需要对多个字段进行变异的情况下,
volatile
关键字仍然会使您容易受到竞争条件的影响。在
ConcurrentHashMap
的情况下,使用了条带化和协作性等高级技术,以在保留原子性的同时将互斥需求降至最低y、 正如你所看到的,这也意味着当一个线程读取一个volatile变量时,它不仅看到了volatile的最新变化,还看到了导致变化的代码的副作用。这似乎是。但是Marko的答案比你的答案清楚得多。我不能同意你的观点。我认为内存模型并不像你提到的那样定义。一个线程怎么可能呢当对象还没有任何引用时访问该对象的字段?有一种方法。它不适用于给定的示例,但这是一个很常见的错误。如果构造函数将
泄露给其他线程,则另一个线程可能会在构造函数调用返回之前访问该对象。在这种情况下e、 Java不保证任何对象字段的初始值对另一个线程立即可见。我只是做了一个实验——我能够在赋值之前访问最终的int。它的值为零,但我想这也不能保证。