如果在 java 中线程 A 在线程 B 之前启动,那么 A 将在 B 之前被 os 调度?
if thread A start before thread B in java,then A will be scheduled by os before B?
我正在阅读《理解 JVM 高级特性和最佳实践》,其中有一段代码解释了 java 中的 happens-before 规则。我无法理解。代码如下:
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()
编辑的结果 return,因为它不是线程安全的。但是书上说如果给函数setValue()
和getValue()
加上synchronized关键字,就不存在线程安全问题,方法getValue()
会return正确的值。书上解释说因为 synchronized
符合 happens-before 规则。所以我通过下面的代码有两个问题。
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
被JVM调度在线程B
之前,但是我们也不能保证指令this.value = value
在return this.value
之前被JVM执行,因为JVM将再次对指令进行排序。我的理解对吗?请帮助我。
"happened before" 的问题不是它导致线程 A 在线程 B 之前设置值。而是尽管线程 A 可能会按时间顺序在线程 B 之前到达 this.value = value
到了运行getValue
,B看到的值可能还是原来的值
也就是说,在线程环境中,即使两条指令按时间顺序执行,也不意味着一条指令的结果会被另一条指令看到。
如果线程B刚好先调用该方法,它总是会得到旧值。但是如果恰好是第二次调用该方法,则不知道是获取旧值还是获取新值。
为此,你不得不用手段来确保"happens before"规则,然后你就知道什么"happened before"的结果被什么"happens after"看到了。
因此,如果 value
是易变的,例如,它确保如果线程 A 在线程 B 之前调用 setValue()
,则线程 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 ║
╚═════════════════════╧════════════════════════╧═════════════════════╝
关于你的两个问题:
- 没错。你不知道他们中的哪一个先得到那个指令。这不仅仅是先调度哪个线程的问题。线程可能 运行ning 在不同的 CPUs 上,一个 CPU 可能需要长时间的内存获取,另一个只需要很短的内存获取,所以它比另一个慢。此外,可能是为代码准备的机器指令的长度不同。通常,您根本不知道幕后发生了什么,并且 Java 不保证线程 运行.
的顺序
- 在这种特殊情况下不太可能重新安排说明,因为这些方法非常短。同样,您无法判断发生了什么,因为它取决于特定的 JVM、CPU 的数量、CPU 的类型、调度程序和内存安排——您无法保证。
向函数添加同步 setValue/getValue
意味着任何想要执行该代码段的线程都必须首先获得(或等待)该对象的锁。
如果我们假设在线程 A 调用 setValue/getValue 之前没有持有锁,线程 A 将立即获得锁。但是,在此期间,如果线程 B 调用 setValue/getValue
,它将必须等待线程 A 放弃锁才能执行该方法。
但是,如果两个线程都在等待对象上的锁,我们无法保证 OS 首先选择哪个线程。
我正在阅读《理解 JVM 高级特性和最佳实践》,其中有一段代码解释了 java 中的 happens-before 规则。我无法理解。代码如下:
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()
编辑的结果 return,因为它不是线程安全的。但是书上说如果给函数setValue()
和getValue()
加上synchronized关键字,就不存在线程安全问题,方法getValue()
会return正确的值。书上解释说因为 synchronized
符合 happens-before 规则。所以我通过下面的代码有两个问题。
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
被JVM调度在线程B
之前,但是我们也不能保证指令this.value = value
在return this.value
之前被JVM执行,因为JVM将再次对指令进行排序。我的理解对吗?请帮助我。
"happened before" 的问题不是它导致线程 A 在线程 B 之前设置值。而是尽管线程 A 可能会按时间顺序在线程 B 之前到达 this.value = value
到了运行getValue
,B看到的值可能还是原来的值
也就是说,在线程环境中,即使两条指令按时间顺序执行,也不意味着一条指令的结果会被另一条指令看到。
如果线程B刚好先调用该方法,它总是会得到旧值。但是如果恰好是第二次调用该方法,则不知道是获取旧值还是获取新值。
为此,你不得不用手段来确保"happens before"规则,然后你就知道什么"happened before"的结果被什么"happens after"看到了。
因此,如果 value
是易变的,例如,它确保如果线程 A 在线程 B 之前调用 setValue()
,则线程 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 ║ ╚═════════════════════╧════════════════════════╧═════════════════════╝
关于你的两个问题:
- 没错。你不知道他们中的哪一个先得到那个指令。这不仅仅是先调度哪个线程的问题。线程可能 运行ning 在不同的 CPUs 上,一个 CPU 可能需要长时间的内存获取,另一个只需要很短的内存获取,所以它比另一个慢。此外,可能是为代码准备的机器指令的长度不同。通常,您根本不知道幕后发生了什么,并且 Java 不保证线程 运行. 的顺序
- 在这种特殊情况下不太可能重新安排说明,因为这些方法非常短。同样,您无法判断发生了什么,因为它取决于特定的 JVM、CPU 的数量、CPU 的类型、调度程序和内存安排——您无法保证。
向函数添加同步 setValue/getValue
意味着任何想要执行该代码段的线程都必须首先获得(或等待)该对象的锁。
如果我们假设在线程 A 调用 setValue/getValue 之前没有持有锁,线程 A 将立即获得锁。但是,在此期间,如果线程 B 调用 setValue/getValue
,它将必须等待线程 A 放弃锁才能执行该方法。
但是,如果两个线程都在等待对象上的锁,我们无法保证 OS 首先选择哪个线程。