假设我有以下课程:
private final int[] ints = new int[5];
public void set(int index, int value) {
ints[index] = value;
}
public int get(int index) {
return ints[index]
}
线程A运行以下内容:
set(1, 100);
(非常)在线程B运行以下之后不久:
get(1)
我的理解是,无法保证线程B会看到线程A所做的更改,因为更改仍然可能位于CPU缓存/寄存器中......或者可能存在指令重新排序... 这是对的吗?
继续前进,如果我有以下课程会发生什么:
public class ImmutableArray {
public final int[] ints
public ImmutableArray(int[] ints) {
this.ints = ints
}
}
使用以下局部变量:
volatile ImmutableArray arr;
和线程A运行以下内容:
int[] ints = new int[5];
int[0] = 1;
arr = new ImmutableArray(ints);
(非常)在线程B运行以下之后不久:
int i = arr.ints[0];
线程B是否保证获得值1
,因为最终和发生在之前的关系,即使数组中的值设置在此之外也是如此?
编辑:在第二个示例中,数组永远不会更改,因此名称为“ImmutableArray”
第二次编辑::
所以我对答案的理解是:鉴于此:
int a = 0
volatile int b = 0;
如果线程A执行以下操作:
a = 1;
b = 1;
然后线程B执行以下操作:
a == 1; // true
b == 1; // true
如此不稳定是一种障碍,但障碍在什么时候结束并允许再次重新排序指令?
答案 0 :(得分:2)
两种情况都是正确的。事实上,即使你放松了问题陈述,你也是对的。
在第一个例子中,无论后来的线程B评估get(1)
多少,都不能保证它会观察线程A对set(1, 100)
的调用所写的值。
在第二个示例中,final
上的ints
或 volatile
上的 arr
将是足以让你有保证观察ints[0] = 5
。如果没有volatile
,您将无法保证观察ImmutableArray
实例本身,但无论何时观察它,您都可以保证在完全构建状态下观察它。更具体地说,保证涉及从构造函数返回时对象的final
字段可到达的完整状态。
答案 1 :(得分:1)
我的理解是,无法保证线程B会看到线程A [制作]
的更改
正确。
线程B是否保证获得值1,因为最终和发生在之前的关系?
没有。当您声明volatile ImmutableArray arr;
时,唯一变为volatile的是对arr
的引用,而不是arr
(arr.ints
)的内容,也不是arr
内容的内容1}}(arr.ints[0]
)。