【发布时间】:2020-08-30 16:44:53
【问题描述】:
考虑以下代码:
public class Resource {
public int val;
public Resource() {
val = 3;
}
}
public class UnsafePublication {
public static Resource resource;
public static void initialize() {
resource = new Resource();
}
}
在线程1中
UnsafePublication.initialize();
在线程2中
while (true) {
if (UnsafePublication.resource != null) {
System.out.println(UnsafePublication.resource.val);
break;
}
}
我们知道不安全的发布可能导致 Thread2 打印 0 而不是 3。
查阅了很多参考资料,总结出两种解释:
- 假设赋值
resource = new Resource();确实写透了,所以Thread2 会发现UnsafePublication.resource不为空。但是Resource的构造函数中的赋值val = 3;没有写透,所以val会是默认的0值。 -
将
new Resource()分配给resource时会重新排序。具体来说:- 为资源对象分配内存
- 调用 Resource 的构造函数来初始化对象
- 将对象分配给“资源”字段
被重新排序为:
- 为资源对象分配内存
- 将对象分配给“资源”字段
- 调用 Resource 的构造函数来初始化对象。
所以如果 Thread1 在重新排序的版本中刚刚完成了 step2,并且 Thread2 被换入,那么 Thread2 会发现
UnsafePublication.resource.val是默认的 0 值。
所以这是我的问题:这两种解释都正确且可能吗?而在现实世界中,这两个因素是否甚至可能混合在一起使情况变得更加复杂?
【问题讨论】:
-
它不会打印
0asresourcewould benull -
@jhamon 是的!由于
resource不是易失性的,因此不保证Thread1 中的更新对Thread2 可见,但有可能。 (如果我没有以错误的方式理解一些参考资料......)在解释1中,我假设resource确实是写透并且对其他线程可见。 -
您的示例代码中没有线程,因此它的行为与您声称的不同。
-
@user207421 在代码块的底部,我comet了两个线程的动作......
标签: java concurrency