我正在阅读“了解JVM高级功能和最佳实践”,其中包含一个代码段,用于解释java中的事先规则。我不明白。代码如下:
private int value = 0;
//executed by Thread A
public void setValue(int value){
this.value = value;
}
//executed by Thread B
public void getValue(){
return value;
}
假设线程A
在代码中的线程B
之前开始。我可以理解,我们不知道线程B中getValue()
返回的结果,因为它不是线程安全的。但该书说如果将同步关键字添加到函数setValue()
和getValue()
,则不存在线程安全问题,方法getValue()
将返回正确的值。这本书解释说,因为synchronized
符合先前发生的规则。所以我在下面的代码中有两个问题。
public class VolatileDemo3 {
private volatile int value = 0;
public static void main(String[] args) {
VolatileDemo3 v = new VolatileDemo3();
Thread A = new Thread(v.new Test1());// Thread A
Thread B = new Thread(v.new Test2());//Thread B
A.start();
B.start();
}
public void setValue(int value){
this.value = value;
}
public int getValue(){
return this.value;
}
public class Test1 implements Runnable {
@Override
public void run() {
setValue(10);
}
}
public class Test2 implements Runnable {
@Override
public void run() {
int v = getValue();
System.out.println(v);
}
}
}
A.start()
在B.start()
之前运行且值为volatile
,但我们无法确保主题B可以打印出10
,对吧?因为线程B可能首先由JVM调度,所以线程B将打印0而不是10。A
在线程B
之前由JVM调度,但我们也无法保证JVM在this.value = value
之前执行的指令return this.value
因为JVM将排序再次说明。我的理解是对的吗?请帮帮我。答案 0 :(得分:9)
“之前发生”的问题并不是它导致线程A在线程B之前设置值。虽然可能发生线程A在线程B运行之前按时间顺序到达onPause
this.value = value
,B看到的值可能仍然是旧值。
也就是说,在线程环境中,即使按时间顺序执行两条指令,也不意味着另一条指令的结果会被另一条指令看到。
如果线程B碰巧首先调用该方法,它将始终获得旧值。但是,如果碰巧将方法称为第二个,那么它是否会获得旧值或新值是未知的。
出于这个原因,你必须使用手段来确保“之前发生”规则,然后你知道“之前发生的事情”的结果可以通过“发生之后”看到。
因此,如果getValue
是易失性的,它确保如果线程B之前的线程A调用value
,则线程B将看到新值。
╔═════════════════════╤════════════════════════╤═════════════════════╗ ║ Order of operations │ Are we using │ What value of value ║ ║ │ volatile/synchronized? │ will B see? ║ ╠═════════════════════╪════════════════════════╪═════════════════════╣ ║ A runs setValue(10) │ N │ Unknown ║ ║ B runs getValue() ├────────────────────────┼─────────────────────╢ ║ │ Y │ 10 ║ ╟─────────────────────┼────────────────────────┼─────────────────────╢ ║ B runs getValue() │ N │ 0 ║ ║ A runs setValue(10) ├────────────────────────┼─────────────────────╢ ║ │ Y │ 0 ║ ╚═════════════════════╧════════════════════════╧═════════════════════╝
关于你的两个问题:
答案 1 :(得分:0)
将synchronized添加到函数setValue/getValue
意味着任何想要执行该段代码的线程首先必须获取(或等待)该对象的锁定。
如果我们假设在线程A调用setValue / getValue之前没有锁定,则线程A将立即获得锁定。但是,在过渡期间,如果线程B调用setValue/getValue
,它必须等待线程A放弃锁定才能执行该方法。
但是,如果两个线程都在等待对象的锁定,我们无法保证操作系统首先选择哪一个。