【发布时间】:2019-10-18 12:16:05
【问题描述】:
我想弄清楚发生前属性的确切含义。
我已经看到了对happens-before属性的解释说,如果全局变量(不是易失性或包含在同步块中)的更新在其他一些易失性变量之前被更改,则它们对其他线程可见,或者在同步块内改变。这是正确的吗?如果是这样,它在 java 文档中的什么地方是这样说的?
我的理解是,happens-before 属性定义了共享字段和代码执行之间的关系,例如:
- 监视器的解锁发生在同一监视器的每次后续锁定之前。
- 对 volatile 字段的写入发生在对同一字段的每次后续读取之前。
- 在线程上启动的调用发生在已启动线程中的任何操作之前。
- 线程中的所有操作都发生在任何其他线程从该线程的连接成功返回之前。
例如:
class Shared {
private int y = 0;
private volatile int x = 0;
public void setOne() {
y = 1;
x = 1;
}
public int getY() {
return y;
}
}
对于上面的代码,给定 2 个线程:
Shared shared = new Shared();
new Thread(() -> shared.setOne()).start();
new Thread(() -> shared.getY()).start();
编辑 假设我们可以保证第一个线程已经启动,那么 getY() 会在这里返回 0 还是 1?
我还看到一些例子说这种行为只发生在读取线程中的 volatile 字段之后。因此,在这种情况下,如果一个线程读取 volatile 字段的值(假设是线程 B),那么线程 A 中在该 volatile 字段之前写入的所有字段都可用于线程 B。据此,如果我修改 getY() 方法上面的共享对象是:
public int getXPlusY() {
int local = x;
return local + y;
}
是这个动作让 y 对其他线程可见吗?
【问题讨论】:
-
看这个,回答你所有的问题:youtube.com/watch?v=pS5dPQwgnYo
-
"getY() 会在这里返回 0 还是 1?"任何一个。不能保证在第二个线程启动时第一个线程已经启动。
-
谢谢@Shadov,我会给它一块手表。
-
@AndyTurner,为了这个例子,假设我们可以保证第一个线程已经启动,它仍然可以吗?我将编辑问题以添加该假设。谢谢。
-
正在启动的线程并不意味着它在任何其他线程可以(部分)执行之前执行到最后。即使在
getY()的开头有一个Thread.yield(),它最终也会返回零