我有以下课程:
public class Example {
private AtomicBoolean isValueSet = new AtomicBoolean(false);
private int value = 0; // I DON'T want to make this volatile.
private void initializeValue(int value) {
// as you can see, value can only ever be set once.
if (isValueSet.compareAndSet(false, true)) {
this.value = value;
}
}
public int getValue() {
return value;
}
}
Example
类的值仅初始化 ONCE ,但必须初始化 AFTER {{strong>的实例1}}已创建。这意味着我无法在构造函数中设置它(使其成为Example
)。 final
是一次写入,多次读取的成员变量。
问题:
我在多线程环境中工作,因此可以在Example.value
上初始化该值并在Thread 1
上读取。这意味着在Thread 2
上设置了值之后,可能没有将写入从缓存刷新到内存,这意味着写入对Thread 1
不可见。
在你进一步阅读之前,我想明确表示,对这个问题的关注不是可能存在的任何竞争条件,而只是确保Thread 2
来电确保该值将写入内存。
虽然这是最简单的解决方案,但我在多线程环境中工作,其中性能是关键所以我 NOT 想要initializeValue()
Example.value
。
问题:
现在我对先发生原则的理解意味着在volatile
块中设置的任何值都会自动刷新到内存中,因此我可以通过以下更改来解决我的问题:
synchronized
这是对的吗?
修改
鉴于我们已经确定上述方法不起作用,请执行以下操作:
private void initializeValue(int value) {
// as you can see, value can only ever be set once.
if (isValueSet.compareAndSet(false, true)) {
synchronized (new Object()) {
this.value = value;
}
}
}
答案 0 :(得分:3)
不,这不对。值的读者必须在同一个对象上同步才能读取值,然后才会应用before-before原则。
监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法条目)之前。并且由于之前发生的关系是可传递的,因此在解锁之前线程的所有操作都会发生 - 在任何线程锁定该监视器之后的所有操作之前。
答案 1 :(得分:2)
您当前的方法不正确,无法保证写入的可见性。我建议您肯定使用volatile变量来保存值,如果volatile读取确实是您的并发应用程序已经证明的瓶颈,请调整设计,使每个线程只读取一次volatile值并将其缓存在某些线程本地存储。另请注意,在写入变量之后启动的线程即使是普通的非易失性变量也能保证看到写入。