通过下面的例子,假设线程A创建对象X,线程B调用方法getY(),变量y是否需要声明为volatile才能对线程B可见?
public class X{
int y;
public X(){
y=10;
}
public int getY(){
return y;
}
}
答案 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
synchronized
块中 如果您使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个元素的列表。
如果您想从多个线程访问某个字段,必须为final
或volatile
,或通过synchronized
阻止访问。