引用文章,是用一对volatile
来防止乱序执行。我想知道,是否可以使用单个 volatile
来阻止它?
void fun_by_thread_1() {
this.isNuclearFactory = true;
this.factory = new NuclearFactory();
}
void fun_by_thread_2() {
Factory _factory = this.factory;
if (this.isNuclearFactory) {
// Do not operate nuclear factory!!!
return;
}
// If out-of-order execution happens, _factory might
// be NuclearFactory instance.
_factory.operate();
}
Factory factory = new FoodFactory();
volatile boolean isNuclearFactory = false;
我问的原因是因为我有一个保护标志(类似于 isNuclearFactory
标志),以防止多个变量(类似于许多 Factory
)。我不希望将所有 Factory
标记为易变的。
或者,我应该陷入以下解决方案吗?
void fun_by_thread_1() {
writer.lock();
try {
this.isNuclearFactory = true;
this.factory = new NuclearFactory();
} finally {
writer.unlock();
}
}
void fun_by_thread_2() {
reader.lock();
try {
Factory _factory = this.factory;
if (this.isNuclearFactory) {
// Do not operate nuclear factory!!!
return;
}
} finally {
reader.unlock();
}
_factory.operate();
}
Factory factory = new FoodFactory();
boolean isNuclearFactory = false;
P/S:我知道instanceof
。 Factory只是一个乱序问题的例子。
最佳答案
您的第一个解决方案存在的问题是,如果 factory
不是易变的,则无法保证可见性。也就是说,如果它在一个线程中发生更改,其他线程可能不会在看到更改后的 isNuclearFactory
值的同时看到更改后的值,或者甚至可能根本看不到它。
所以有可能
- 线程 A 调用
fun_by_thread_1()
- 线程 B 调用
fun_by_thread_2()
并且它看到isNuclearFactory == true
,但是仍然看到factory
的先前值,这可能为null
,或者可以指代非核工厂。
幸运的是,解决这个特殊问题很简单:反转 fun_by_thread_1()
中的赋值顺序。
void fun_by_thread_1() {
this.factory = new NuclearFactory();
this.isNuclearFactory = true;
}
改变 volatile 值会传播到所有其他线程;此外,它还保证了在接触 volatile 变量之前当前线程所做的所有更改的可见性。
然而,这引入了一个新的数据竞争问题:现在有可能
- 线程 B 调用
fun_by_thread_2()
并获取对factory
的引用,它恰好指向一个非核工厂 - 线程 A 调用
fun_by_thread_1()
- 线程 B 测试
isNuclearFactory
并发现它是true
(由线程 A 设置),因此它返回时不使用_factory
,尽管它指非核工厂!
也有可能
- 线程 A 调用
fun_by_thread_1()
并创建一个新的核工厂 - 线程 B 调用
fun_by_thread_2()
并获得对factory
的引用,它恰好指向新的核工厂 - 线程B测试
isNuclearFactory
,发现还是false
,于是操作核_factory
!
所以我对你的标题问题的回答是否。事实上,即使有两个 volatile
变量,您也很容易遇到问题。每当您有两个不同但逻辑相关的变量暴露给多个线程时,最好的解决方案是将它们封装到一个对象中,这样您就可以通过更改单个 (volatile
)引用。
关于java - 是否可以通过使用单个 volatile 来防止乱序执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4852758/