gst*_*low 4 java concurrency visibility final java-memory-model
我尝试理解最终字段的语义.
让研究代码:
public class App {
final int[] data;
static App instance;
public App() {
this.data = new int[]{1, 0};
this.data[1] = 2;
}
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
instance = new App();
}
}).start();
while (instance == null) {/*NOP*/}
System.out.println(Arrays.toString(instance.data));
}
}
Run Code Online (Sandbox Code Playgroud)
我有一些问题:
PS我不知道如何使标题正确,随时编辑.
如果我们更换,是否有可见性差异:
public App() {
this.data = new int[]{1, 0};
this.data[1] = 2;
}
Run Code Online (Sandbox Code Playgroud)
同
public App() {
int [] data = new int[]{1, 0};
data[1] = 2;
this.data = data;
}
Run Code Online (Sandbox Code Playgroud)
另外我想知道wjat将final 在我的例子中替换为volatile.
因此,我想得到关于4个新病例的解释
是的,如果应用程序永远终止,它将输出[1,2].关键是final字段语义作为一个整体应用于构造函数,将数组引用写入字段时的确切时间是无关紧要的.这也意味着在构造函数中,重新排序是可能的,因此如果this引用在构造函数完成之前转义,则所有保证都是无效的,无论是this在程序顺序中写入之前还是之后转义.由于在您的代码中,this在构造函数完成之前不会转义,因此保证适用.
当构造函数完成时,对象被认为是完全初始化的.在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象
final字段的正确初始化值.
请注意,它指的是完全初始化的状态,而不是对特定final字段的写入.这也将在下一节§17.5.1中讨论:
设o为对象,c为o的构造函数,其中写入
final字段f.当c退出时,正常或突然发生对ofinal场f的冻结动作.
如果将变量更改为volatile,则几乎不提供任何保证.一个volatile领域建立之前发生该变量写入和后续读取之间的关系,但往往忽略了关键的一点是词" 后续 ".如果App实例未正确发布,就像在您的示例中一样,则不保证主线程的读取instance.data将是后续的.如果它读取null现在可能的引用,那么您知道它不是后续的.如果它读取非null引用,则表示它是在字段写入之后,这意味着您可以保证1在第一个插槽中读取,但是对于第二个插槽,您可能会读取0或2.
如果你想在屏障和重新排序方面讨论这个问题,volatile写入data保证所有先前的写入都被提交,包括写入1第一个数组插槽,但它不保证后续的非volatile写入不会提前.因此,仍然可能App在volatile写入之前执行不正确的引用发布(尽管很少发生).
如果将写入移动到构造函数的末尾,则一旦null看到非数组引用,所有先前的写入都是可见的.对于final字段,它不需要进一步讨论,如上所述,无论如何,写入在构造函数中的实际位置是无关紧要的.对于这种volatile情况,如上所述,您不能保证读取非null引用,但是当您读取它时,所有先前的写入都将被提交.知道表达式new int[]{1, 0};被编译为等价的方法可能会有所帮助hiddenVariable=new int[2]; hiddenVariable[0]=1; hiddenVariable[1]=0;.在构造之后但在将volatile数组引用写入字段之前放置另一个数组,不会改变语义.
| 归档时间: |
|
| 查看次数: |
195 次 |
| 最近记录: |