重要编辑:我知道关于“发生在”在进行两个赋值的线程中,我的问题是另一个线程是否可能在"a"仍然为null的情况下读取非空的"b"。所以我知道如果你从与之前调用setBothNonNull(...)的线程相同的线程中调用doIt(),它不会抛出NullPointerException。但是如果有人从另一个线程中调用doIt(),而不是调用setBothNonNull(...)的那个线程呢?
请注意,本问题仅涉及volatile
关键字和volatile
保证:它与synchronized
关键字无关(因此请不要回答“您必须使用synchronize”,因为我没有任何问题需要解决:我只想了解关于乱序执行的volatile
保证(或缺乏保证))。
假设我们有一个对象包含两个volatile
字符串引用,它们由构造函数初始化为null,并且我们只有一种方法来修改这两个字符串:通过调用setBoth(...),并且之后只能将它们的引用设置为非null引用(只允许构造函数将它们设置为null)。
例如(这只是一个示例,还没有问题):
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内存模型的语义,而不是解决问题)。
b
之前分配a
,那么有可能会得到一个非空的b
,但仍然得到一个空的a
。现在,使用volatile
有哪些保证呢?确实是个好问题+1。 - kiwicptn