【发布时间】:2016-07-18 17:34:27
【问题描述】:
情况如下:
- 我有一个包含很多 setter 和 getter 的对象。
- 此对象的实例是在一个特定线程中创建的,其中所有值都已设置。最初,我使用 new 语句创建了一个“空”对象,然后我才根据一些复杂的遗留逻辑调用一些 setter 方法。
- 只有这样,该对象才可供所有其他仅使用 getter 的线程使用。
问题:我是否必须让这个类的所有变量都为volatile?
关注点:
- 创建对象的新实例并设置其所有值 在时间上是分开的。
- 但所有其他线程对此一无所知 新实例,直到设置所有值。所以其他线程不应该 缓存未完全初始化的对象。不是吗?
注意:我知道构建器模式,但由于其他几个原因,我不能在那里应用它:(
已编辑: 由于我觉得 Mathias 和 axtavt 的两个答案不太匹配,所以我想补充一个例子:
假设我们有一个foo 类:
class Foo {
public int x=0;
}
如上所述,两个线程正在使用它:
// Thread 1 init the value:
Foo f = new Foo();
f.x = 5;
values.add(f); // Publication via thread-safe collection like Vector or Collections.synchronizedList(new ArrayList(...)) or ConcurrentHashMap?.
// Thread 2
if (values.size()>0){
System.out.println(values.get(0).x); // always 5 ?
}
据我了解 Mathias,它可以根据 JLS 在某些 JVM 上打印出 0。据我了解,axtavt 总是会打印 5。
你的意见是什么?
-- 问候, 德米特里
【问题讨论】:
-
我无法确定哪个答案是正确的。想听听其他参与者的意见。
-
这两个答案是兼容的。 axtavt 不假设线程何时启动,因此它解决了一般情况。 Mathias 解决了如果您确实知道您的对象已构建并且在线程启动之前设置了它的值会发生什么(用 axtavt 的话说,这可以被视为 安全发布 的一个特例,这要归功于线程开始强制执行的可见性障碍)。它们相互补充(如果您的线程在设置值之前启动,请注意您将
volatiles 放在哪里)。
标签: java concurrency volatile