从未赋予null的volatile变量是否可以包含null?

时间:2015-12-17 18:17:05

标签: java multithreading volatile

可以在以下概念Java示例中使用:

public class X implements Runnable {
    public volatile Object x = new Object();

    @Runnable
    public void run() {
        for (;;) {
            Thread.sleep(1000);
            x = new Object();
        }
    }
}

x从另一个帖子中被视为null

额外奖励:我是否需要声明它是不稳定的(我真的不关心那个值,将来某个时候它将是新指定的值并且永远不会为空)

3 个答案:

答案 0 :(得分:4)

技术上,是的,它可以。这是原ConcurrentHashMap's readUnderLock的主要原因。 javadoc甚至解释了如何:

  

读取锁定条目的值字段。如果值字段似乎为空,则调用。只有当编译器碰巧使用其表分配重新排序HashEntry初始化时才有可能,这在内存模型下是合法的,但是不知道会发生什么。

由于HashEntry的value是易变的,因此这种类型的重新排序在构造方面是合法的。

故事的道德是所有非最终的初始化都可以与对象构建竞争。

编辑: @Nathan Hughes问了一个有效的问题:

  

@John:在OP的例子中,在运行runnable的线程开始之前,构造是否会发生?看起来这会在场的初始化之后强加一个先发生的障碍。

Doug Lea对这个话题有几点评论,整个帖子可以是read here。他回答了评论:

  

但问题在于是否必须在volatile存储之后将新C实例分配给其他内存。

回答

  

很抱歉错误记住为什么我已经基本解决了这个问题:   除非JVM总是预先存储内存(通常不是一个好选项),否则   即使未明确初始化,也必须将volatile字段归零   在构造函数体中,在发布之前使用释放围栏。   即使有些情况下JMM没有   严格要求机械师防止出版物重新排序   在具有volatile字段的类的构造函数中,唯一的好处   JVM的实现选择要么使用非易失性写入   使用尾随释放栅栏,或执行每个易失性写入   完全击剑。无论哪种方式,都没有与出版物重新排序。   不幸的是,程序员不能依赖规范来保证   它,至少在JMM修订之前。

结束时:

  
      
  • 程序员并不期望即使最终字段是具体的   出版物安全,易变的领域并非总是如此。

  •   
  • 出于各种实现原因,JVM安排这样做   无论如何,易失性领域是安全的,至少在   我们知道的案例。

  •   
  • 实际上更新JMM / JLS以强制执行此操作并非易事   (我知道适用的小调整)。但现在是个好时机   考虑对JDK9进行全面修订。

  •   
  • 同时,进一步测试是有意义的   并验证JVM是否符合这个可能的未来规范。

  •   

答案 1 :(得分:2)

这取决于X实例的发布方式。

假设x发布不安全,例如。通过非volatile字段

private X instance;
...
void someMethod() {
    instance = new X();
}

允许访问instance字段的另一个线程看到引用未初始化的X对象的引用值(即,其构造函数尚未运行的位置)。在这种情况下,其字段x的值为null

以上示例转换为

temporaryReferenceOnStack = new memory for X // a reference to the instance
temporaryReferenceOnStack.<init> // call constructor
instance = temporaryReferenceOnStack;

但该语言允许以下重新排序

temporaryReferenceOnStack = new memory for X // a reference to the instance
instance = temporaryReferenceOnStack;
temporaryReferenceOnStack.<init> // call constructor

或直接

instance = new memory for X // a reference to the instance
instance.<init> // call constructor

在这种情况下,允许线程在调用构造函数初始化引用的对象之前查看instance的值。

现在,在当前的JVM中发生这种情况的可能性有多大?呃,我无法想出一个MCVE。

  

奖金:我是否需要声明它不稳定(我真的不在乎   这个价值,它将来的某个时候就足够了   新分配的值,永远不为空)

安全地发布封闭对象。或者使用您final的{​​{1}} AtomicReference字段。

答案 2 :(得分:1)

没有。 Java内存模型保证您永远不会将x视为null。 x必须始终是分配的初始值或后续值。

这实际上适用于任何变量,而不仅仅是volatile。您所询问的内容被称为“凭空价值”。 C.F. Java Concurrency in Practice ,它在一定程度上讨论了这个概念。

你的问题的另一部分“我是否需要声明x为volatile:”给定上下文,是的,它应该是volatilefinal.任何一个提供安全发布对于x引用的对象。 C.F. Safe Publication.显然,如果xfinal,则conjugatePresentAr()以后就无法更改。