如果在构造函数中赋值一次,则原始变量需要是volatile

时间:2015-12-30 00:06:27

标签: java volatile

通过下面的例子,假设线程A创建对象X,线程B调用方法getY(),变量y是否需要声明为volatile才能对线程B可见?

public class X{
   int y;

   public X(){
      y=10;
   }

   public int getY(){
     return y;
   }
}

4 个答案:

答案 0 :(得分:1)

  

如果在构造函数

中赋值一次,原始变量是否需要是易变的

总之,是的。 (但也有其他解决方案。)

此处可能会影响您的方案是y = 10赋值可能无法刷新,因此第二个线程可能会看到默认的初始值(0)而不是{{1} }。

可能的解决方案:

  • 10声明为y。如果final的实例在构造函数结束 1 之前未被“发布”,则JLS保证其他线程将看到X的正确值。

  • y声明为y。缺点是这会在>>所有<<之前增加内存屏障。读到volatile;即每次y电话。

  • 确保在构造函数在线程A中完成并且在线程B调用getY()之前,两个线程之间存在发生在之前。这可以通过:

    • getY实例从A传递到X变量,
    • 使用同步方法调用或
    • 传递它
    • 让线程volatile在{B}线程上调用A之前将共享X实例提供给B

1 - ...并且您不使用讨厌的反射,start()或本机代码来打破Unsafe

答案 1 :(得分:0)

如果

,您不需要使用volatile
  1. 变量是最终的
  2. 变量仅由一个线程使用
  3. 变量仅在synchronized块中
  4. 如果您使y成为最终版,则可以将代码更改为案例1。否则你应该让它变得不稳定。

答案 2 :(得分:0)

是。

如果变量未声明为volatile,则可以在任何线程中缓存。

但是 - 如果变量被声明为final,则会出现其他注意事项。

答案 3 :(得分:0)

Java内存模型仅为对象的final字段提供初始化安全性,因此理论上,其他线程可以看到非最终字段的默认值。对于更复杂的对象,他们可以看到对象处于部分构造状态。

例如,如果您的代码是

public class X{
   List<Integer> y;

   public X(){
      y=Arrays.asList(new Integer[]{1, 2, 3, 4});
   }

   public List<Integer> getY(){
     return y;
   }
}

然后你可能会发现你的线程B看到了一个包含少于4个元素的列表。

如果您想从多个线程访问某个字段,必须finalvolatile,或通过synchronized阻止访问。