Java 该变量是否需要声明为volatile?

Java 该变量是否需要声明为volatile?,java,multithreading,volatile,Java,Multithreading,Volatile,MyThread类中的out变量是否需要在此代码中声明为volatile,或者ThreadTest类中的stdout变量的“volatile”是否会继续 import java.io.PrintStream; class MyThread implements Runnable { int id; PrintStream out; // should this be declared volatile? MyThread(int id, PrintStream out

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

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.io.PrintStream;
类MyThread实现可运行
{
int-id;
PrintStream out;//应该声明为volatile吗?
MyThread(int-id,PrintStream-out){
this.id=id;
this.out=out;
}
公开募捐{
试一试{
Thread.currentThread().sleep((int)(1000*Math.random());
out.println(“线程”+id);
}
捕捉(中断异常e){
e、 printStackTrace();
}
}
}
公共类线程测试
{
静态volatile PrintStream stdout=System.out;
公共静态void main(字符串[]args){
对于(int i=0;i<10;i++){
新线程(新的MyThread(i,stdout)).start();
}
}

}
它肯定不会延续下去。不过我不知道你想做什么
volatile
限定ThreadTest中的字段,而不是值

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

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();
        }
    }
}
波动性不会“结转”,因为您实际上传递了变量的值

根据我对规范的阅读,如果要在创建对象的线程和使用对象的线程之间不进行同步的情况下读取
数据,则需要
volatile

在编写的代码中,风险变量为
out
。这是一个非私有变量,可以通过任何 有权访问该类。在您的示例中没有这样做的代码,但是您可以编写另一个类。。。或更改
ThreadTest

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

  • out
    声明为
    final
    。这意味着不需要同步

  • out
    声明为
    private
    。现在,线程构造和
    start()
    调用之间的“before”可以确保只有访问
    out
    才能看到正确的值


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


需要明确的是,volatile应用于变量,而不是引用的对象。

不是必需的。

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