【问题标题】:Does this variable need to be declared volatile?这个变量需要声明为 volatile 吗?
【发布时间】:2010-05-29 05:28:48
【问题描述】:

MyThread 类中的out 变量是否需要在此代码中声明为 volatile,或者 ThreadTest 类中的stdout 变量的“波动性”是否会延续?

import java.io.PrintStream;

class MyThread implements Runnable
{
    int id;
    PrintStream out; // should this be declared volatile?

    MyThread(int id, PrintStream out) {
        this.id = id;
        this.out = out;
    }

    public void run() {
        try {
            Thread.currentThread().sleep((int)(1000 * Math.random()));
            out.println("Thread " + id);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadTest
{
    static volatile PrintStream stdout = System.out;

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new MyThread(i, stdout)).start();
        }
    }
}

【问题讨论】:

    标签: java multithreading volatile


    【解决方案1】:

    volatile 限定符不会通过,并且在上面的代码中没有任何作用。 Volatile 在对变量的读取和写入时建立了一个内存屏障,一旦在构造函数中初始化,就永远不会修改。出于同样的原因,ThreadTest 中的 volatile 限定符也没有任何作用。

    为了清楚起见,volatile 适用于 变量,而不适用于被引用的对象。

    【讨论】:

    • Ummm ...我对 JLS 内存模型规范的阅读是,在构造函数中写入(非易失性、非最终)字段和读取它之间没有“发生之前”的关系在另一个线程中。因此,示例中的 volatile 限定符是非冗余的。
    • @Stephen C,在构造函数返回之前,其他线程不可能访问变量。
    • 除非对象被构造函数“不安全地发布”(这里不是这种情况),否则不会。但是这里我们说的是构造函数返回后另一个线程访问变量;即在run 方法中。
    • 对不起,斯蒂芬,但我只是没看到。您构造对象,设置变量一次,永远,然后将其传递给另一个线程,该线程访问未更改且永远不会更改的同一变量。在这张图片中,volatile 有保证吗?
    • @Marcelo - 问题是 JLS 中没有任何内容表明构造函数会将变量内容刷新到主内存。因此,如果另一个线程在不同的处理器上执行,它在读取时可能会得到一个过时的out 副本。这就是 Java 内存模型的全部意义所在。我强烈建议您阅读我的答案链接到的 JLS 部分。
    【解决方案2】:

    MyThread 类中的 out 变量是否需要在此代码中声明为 volatile,或者 ThreadTest 类中的 stdout 变量的“波动性”是否会延续?

    波动性不会“延续”,因为您实际上传递了变量的值。

    根据我对JLS memory model 规范的阅读,如果您要阅读out,则需要volatile ,而无需在创建对象的线程和创建对象的线程之间进行一些干预同步。使用它。

    在编写的代码中,有风险的变量是out。这是一个非私有变量,可以被任何东西访问/更新 可以访问该类。您的示例中没有执行此操作的代码,但您可以编写另一个类...或更改ThreadTest

    但在这种情况下,更好的解决方案是:

    • out 声明为finalsemantics of final fields 表示不需要同步。

    • out 声明为private。现在线程构造和start() 调用之间的“之前发生”确保了对out 的唯一可能访问将看到正确的值。

    【讨论】:

      【解决方案3】:

      它肯定不会延续。不过,我不确定您要做什么。 volatile 限定 ThreadTest 中的字段,而不是值。

      【讨论】:

        【解决方案4】:

        没有必要。

        因为之间有 happens-before 在调用 Thread.start 之前和调用 Thread.start

        之后创建的对象

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-16
          • 1970-01-01
          • 2011-04-23
          • 1970-01-01
          相关资源
          最近更新 更多