重要编辑我知道两个分配发生的线程中的“之前发生”我的问题是另一个线程是否有可能在“a”仍然为空时读取“b”非空。所以我知道,如果你从与之前调用setBothNonNull(...)的线程相同的线程调用doIt() ,那么它不能抛出 NullPointerException。但是,如果从另一个线程调用doIt()而不是调用setBothNonNull(...)怎么办?
请注意,此问题仅与volatile
关键字和volatile
保证有关:与关键字无关(因此请不要synchronized
回答“您必须使用同步”,因为我没有任何问题要解决:我只是想了解volatile
保证(或缺乏保证)关于乱序执行)。
假设我们有一个包含两个volatile
String 引用的对象,这些引用由构造函数初始化为 null,并且我们只有一种方法来修改这两个 String:通过调用setBoth(...)并且我们只能在之后将它们的引用设置为非空引用(仅允许构造函数将它们设置为空)。
例如(这只是一个例子,还没有问题):
public class SO {
private volatile String a;
private volatile String b;
public SO() {
a = null;
b = null;
}
public void setBothNonNull( @NotNull final String one, @NotNull final String two ) {
a = one;
b = two;
}
public String getA() {
return a;
}
public String getB() {
return b;
}
}
在setBothNoNull(...)中,分配非空参数“a”的行出现在分配非空参数“b”的行之前。
然后,如果我这样做(再一次,毫无疑问,接下来是问题):
doIt() {
if ( so.getB() != null ) {
System.out.println( so.getA().length );
}
}
我的理解是否正确,由于无序执行我可以获得NullPointerException?
换句话说:不能保证因为我读到了一个非空的“b”,我就会读到一个非空的“a”?
因为由于无序(多)处理器和工作方式volatile
“b”可以在“a”之前分配?
volatile
保证在写入之后读取始终会看到最后写入的值,但是这里有一个无序的“问题”,对吗?(再一次,“问题”是故意试图理解volatile
关键字和 Java 内存模型的语义,而不是解决问题)。