我试图简单地测试 JLS 保证的最终字段的初始化安全性。这是为了我正在写的一篇论文。但是,根据我当前的代码,我无法让它“失败”。有人可以告诉我我做错了什么,或者这只是我必须一遍又一遍地运行然后在一些不幸的时机看到失败的事情吗?
这是我的代码:
public class TestClass {
final int x;
int y;
static TestClass f;
public TestClass() {
x = 3;
y = 4;
}
static void writer() {
TestClass.f = new TestClass();
}
static void reader() {
if (TestClass.f != null) {
int i = TestClass.f.x; // guaranteed to see 3
int j = TestClass.f.y; // could see 0
System.out.println("i = " + i);
System.out.println("j = " + j);
}
}
}
我的线程这样称呼它:
public class TestClient {
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
Thread writer = new Thread(new Runnable() {
@Override
public void run() {
TestClass.writer();
}
});
writer.start();
}
for (int i = 0; i < 10000; i++) {
Thread reader = new Thread(new Runnable() {
@Override
public void run() {
TestClass.reader();
}
});
reader.start();
}
}
}
我已经运行过这个场景很多很多次了。我当前的循环生成了 10,000 个线程,但我已经完成了 1000 个、100000 个甚至 100 万个线程。还是没有失败。我总是看到 3 和 4 这两个值。我怎样才能让这个失败?
我写了规范。 TL;这个答案的 DR 版本只是因为它may看到 y 为 0,这并不意味着它是保证y 见 0。
在这种情况下,最终的字段规范保证您将看到 x 为 3,正如您所指出的。将编写器线程视为有 4 条指令:
r1 = <create a new TestClass instance>
r1.x = 3;
r1.y = 4;
f = r1;
您可能看不到 x 的 3 的原因是编译器重新排序了此代码:
r1 = <create a new TestClass instance>
f = r1;
r1.x = 3;
r1.y = 4;
在实践中通常实现最终字段的保证的方式是确保构造函数在任何后续程序操作发生之前完成。想象一下有人在 r1.y = 4 和 f = r1 之间竖起了一个大障碍。因此,在实践中,如果您有某个对象的任何最终字段,您可能会获得所有这些字段的可见性。
现在,从理论上讲,有人可以编写一个不以这种方式实现的编译器。事实上,许多人经常谈论通过编写最恶意的编译器来测试代码。这在 C++ 人员中尤其常见,他们的语言中有很多未定义的角落,可能会导致可怕的错误。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)