如果线程A在java中的线程B之前启动,则A将在B之前由os调度?

sun*_*nny 5 java multithreading happens-before

我正在阅读"了解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;
}
Run Code Online (Sandbox Code Playgroud)

假设A线程B在代码中的线程之前启动.我可以理解,我们不知道getValue()线程B 返回的结果,因为它不是线程安全的.但是,这本书说,如果添加的同步关键字的功能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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 虽然A.start()之前运行B.start()和值是volatile,我们无法确保线程B可以打印出来10,对吧?因为线程B可能首先由JVM调度,所以线程B将打印0而不是10.
  2. 即使A线程B由JVM 调度之前的线程,但我们也无法保证this.value = valueJVM之前执行的指令return this.value因为JVM会再次排序指令.我的理解是对的吗?请帮我.

Rea*_*tic 9

"之前发生"的问题不是它导致线程A在线程B之前设置值.虽然可能发生线程A this.value = value在线程B运行之前按时间顺序到达getValue,但是B看到的值仍然可能是旧的价值.

也就是说,在线程环境中,即使按时间顺序执行两个指令,也不意味着一个指令的结果将被另一个指示.

如果线程B碰巧首先调用该方法,它将始终获得旧值.但是,如果碰巧将方法称为第二个,那么它是否会获得旧值或新值是未知的.

出于这个原因,你必须使用手段来确保"之前发生"规则,然后你知道"之前发生的事情"的结果可以通过"发生之后"看到.

因此,如果value是volatile,例如,它确保setValue()在线程B之前由线程A调用if ,则线程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. 真正.您无法知道他们中的哪一个首先获得该指令.这不仅仅是首先安排哪个线程的问题.线程可能在不同的CPU上运行,一个CPU可能需要长内存提取,另一个CPU只需要很短的内存提取,因此它比另一个慢.而且,准备代码的机器指令可能具有不同的长度.一般来说,您根本不知道幕后发生了什么,而Java并不保证线程运行的顺序.
  2. 在这种特殊情况下,指令不太可能被重新安排,因为这些方法非常短.同样,你无法分辨出发生了什么,因为它取决于特定的JVM,CPU的数量,CPU的类型,调度程序和内存安排 - 你无法保证.