这两个问题.我们从第二个开始吧.
将新构造的对象分配给易变量变量工作得很好.读取volatile变量的每个线程都将看到一个完全构造的对象.不需要进一步的同步.这种模式通常与不可变类型结合使用.
class Tree {
private volatile Node node;
public void update() {
node = new Node(...);
}
public Node get() {
return node;
}
}
关于第一个问题您可以使用volatile变量来同步对非易失性变量的访问.以下列表显示了一个示例.假设两个变量被初始化,如图所示,并且这两个方法是并发执行的.这是有保证的,如果第二个线程看到更新到foo,它也将看到更新bar.
volatile int foo = 0;
int bar = 0;
void thread1() {
bar = 1;
foo = 1; // write to volatile variable
}
void thread2() {
if (foo == 1) { // read from volatile variable
int r = bar; // r == 1
}
}
但是,你的例子是不同的.阅读和写作可能如下.与上述示例相反,两个线程都从volatile变量读取.但是,对volatile变量的读操作不会相互同步.
void thread1() {
Node temp = root; // read from volatile variable
temp.numOfKeys = 1;
}
void thread2() {
Node temp = root; // read from volatile variable
int r = temp.numOfKeys;
}
换句话说:如果线程A写入volatile变量x并且线程B读取写入x的值,则在读操作之后,线程B将看到在写入x之前发生的线程A的所有写操作.但是没有对volatile变量的写入操作,对其他变量的更新没有影响.
这听起来比实际上更复杂.其实,只有一条规则要考虑,你可以在JLS8 §17.4.5找到:
[..] If all sequentially consistent executions are free of data races,[..] then all executions of the program will appear to be sequentially consistent.
简单地说,如果两个线程可以同时访问相同的变量,则存在数据竞争,至少有一个操作是写入操作,并且该变量是非易失性的.可以通过将共享变量声明为volatile来消除数据竞赛.没有数据竞争,更新的可见性没有问题.