Mr_*_*s_D 13 java multithreading volatile java-memory-model
...没有额外的同步?下面的Tree类应该由多个线程访问(它是单例但不通过枚举实现)
class Tree {
private volatile Node root;
Tree() {
root = new Node();
// the threads are spawned _after_ the tree is constructed
}
private final class Node {
short numOfKeys;
}
}
Run Code Online (Sandbox Code Playgroud)
numOfKeys读取器线程是否可以看到对该字段的更新而没有任何明确的同步(请注意,读者和编写者必须获取ReentrantReadWriteLock每个节点的相同实例 - 但禁止这样做)?如果不能使numOfKeys挥发性足够吗?root = new Node()(除了调用Tree构造函数的主线程外,只有编写器线程会更改根)有关:
编辑:对后5 Java语义感兴趣
SLa*_*aks 10
没有.
在volatile字段中放置对象的引用不会以任何方式影响对象本身.
一旦从volatile字段加载对象的引用,就会有一个与任何其他对象没有区别的对象,并且波动率没有进一步的影响.
有两个问题。让我们从第二个开始。
将新构造的对象分配给volatile变量效果很好。读取volatile变量的每个线程都将看到一个完全构造的对象。无需进一步同步。通常将这种模式与不可变类型结合使用。
class Tree {
private volatile Node node;
public void update() {
node = new Node(...);
}
public Node get() {
return node;
}
}
Run Code Online (Sandbox Code Playgroud)
关于第一个问题。您可以使用易失性变量来同步对非易失性变量的访问。以下清单显示了一个示例。想象一下,如图所示初始化了两个变量,并且两个方法同时执行。可以保证,如果第二个线程看到到的更新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
}
}
Run Code Online (Sandbox Code Playgroud)
但是,您的示例是不同的。阅读和写作可能如下所示。与上面的示例相反,两个线程均从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;
}
Run Code Online (Sandbox Code Playgroud)
换句话说:如果线程A向易失性变量x写入并且线程B读取了写入x的值,则在读取操作之后,线程B将看到线程A的所有写入操作,这些操作在写入x之前发生。但是,如果不对易失性变量进行写操作,则不会影响对其他变量的更新。
听起来比实际要复杂。实际上,只有一条要考虑的规则,您可以在JLS8§17.4.5中找到:
[..]如果所有顺序一致的执行都没有数据争用,则[..]则该程序的所有执行将看起来是顺序一致的。
简而言之,如果两个线程可以同时访问同一个变量,并且至少一个操作为写操作,并且该变量为非易失性,则存在数据争用。通过将共享变量声明为volatile可以消除数据争用。没有数据竞争,更新的可见性就不会有问题。
| 归档时间: |
|
| 查看次数: |
7107 次 |
| 最近记录: |