Java的volatile关键字是关于引用树的"递归",还是必须将每个引用声明为volatile?

adr*_*mir 5 java multithreading volatile

考虑以下玩具示例类:

public class Test {

    private volatile Outer outerVar = new Outer();

    static class Outer {
        Inner innerVar = new Inner();
    }

    static class Inner {

        // state

        // setters

        // getters

    }

    private void multithreadedUse() {
        // play with outerVar.innerVar
    }
}
Run Code Online (Sandbox Code Playgroud)

outerVar是易失性的,因此可能正在使用它的所有线程都会看到它处于相同的状态.但是outerVar.innerVar怎么样?它的父对象(outerVar)被标记为volatile的事实是否也使它变得不稳定?

或者我们必须显式声明innerVar volatile吗?

Joh*_*int 6

但是outerVar.innerVar怎么样?它的父对象(outerVar)被标记为volatile的事实是否也使它变得不稳定?

在这个例子outerVar.innerVar中将正确发布,但它不会是不稳定的.如果您outerVar.innerVar = new Inner()稍后分配,则将丢失线程安全的发布.

这里的规则是在易失性写入之后发生的所有写入在易失性写入之后都是可见的.在写入之后,所有正常写入现在都是线程不安全的.

所以在你的例子中,线程的排序会看到类似的东西

volatile Outer outerVar;
Outer temp = new Outer();
temp.innerVal = new Inner()
outerVar = temp;
Run Code Online (Sandbox Code Playgroud)

注意挥发性写入outVar = temp.这是同步开始的地方.当另一个线程读取非null的outerVar实例时,将安全地发布innerVar字段.

但重申一下,任何时候为自己分配新值outerVar.innerVal都会失去同步性.同样,如果innerVal有任何字段[在初始易失写入后]写入那些字段将无法正确同步

所以回答你的问题

Java的volatile关键字是关于引用树的"递归",还是必须将每个引用声明为volatile?

每个字段必须声明为volatile,在初始易失性写入之后会发生变化(从技术上讲).话虽这么说,如果你在线程之间共享,你应该声明volatile或final字段.