java - 是否可以通过使用单个 volatile 来防止乱序执行

标签 java multithreading

引用文章,是用一对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 值的同时看到更改后的值,或者甚至可能根本看不到它。

所以有可能

  1. 线程 A 调用 fun_by_thread_1()
  2. 线程 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 变量之前当前线程所做的所有更改的可见性。

然而,这引入了一个新的数据竞争问题:现在有可能

  1. 线程 B 调用 fun_by_thread_2() 并获取对 factory 的引用,它恰好指向一个非核工厂
  2. 线程 A 调用 fun_by_thread_1()
  3. 线程 B 测试 isNuclearFactory 并发现它是 true(由线程 A 设置),因此它返回时不使用 _factory,尽管它指非核工厂!

也有可能

  1. 线程 A 调用 fun_by_thread_1() 并创建一个新的核工厂
  2. 线程 B 调用 fun_by_thread_2() 并获得对 factory 的引用,它恰好指向新的核工厂
  3. 线程B测试isNuclearFactory,发现还是false,于是操作核_factory!

所以我对你的标题问题的回答是。事实上,即使有两个 volatile 变量,您也很容易遇到问题。每当您有两个不同但逻辑相关的变量暴露给多个线程时,最好的解决方案是将它们封装到一个对象中,这样您就可以通过更改单个 (volatile)引用。

关于java - 是否可以通过使用单个 volatile 来防止乱序执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4852758/

相关文章:

Java,循环遍历ArrayList

c++ - Pthreads 类似信号的暂停?

c++ - ActiveMQ线程安全吗?

java - 从 Spring Boot 资源执行和处理 Void @Async 操作

java - jSTL <c :when> tag wrong result

java - 不使用 JavaFX 时使用 FXCollections 是否干净/正确

java - Spark CSV - 找不到实际参数的适用构造函数/方法

c# - 为什么每个人都说 SpinLock 更快?

python - 处理两个传入数据流并将它们组合在 python 中?

java - Java中使用线程进行套接字编程