繁体   English   中英

如果线程A在Java中的线程B之前开始,那么A将由os在B之前调度?

[英]if thread A start before thread B in java,then A will be scheduled by os before B?

我正在阅读《了解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);
        }
    }
}
  1. 尽管A.start()B.start()之前运行并且value是volatile ,但我们不能确保线程B可以打印出10 ,对吗? 因为可以由JVM首先调度线程B,所以线程B将输出0而不是10。
  2. 即使JVM将线程A安排在线程B之前,但我们也不能保证指令this.value = value由JVM在return this.value之前执行的this.value = value ,因为JVM会再次对指令进行排序。 我理解正确吗? 请帮我。

“之前发生”的问题不是因为它导致线程A在线程B之前设置了值。这是尽管线程A按时间顺序在线程B必须运行getValue之前按顺序到达了this.value = value , B看到的值可能仍然是旧值。

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

如果线程B碰巧首先调用该方法,它将始终获得旧值。 但是,如果碰巧要第二次调用该方法,则无法确定它是旧值还是新值。

因此,您必须使用手段来确保“发生在……之前”规则,然后您知道“发生在……之前”的结果被“发生在……之后”的事物所看到。

因此,例如,如果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                   ║
╚═════════════════════╧════════════════════════╧═════════════════════╝

关于您的两个问题:

  1. 真正。 您不知道其中哪一个先进入该指令。 这不仅仅是首先安排哪个线程的问题。 线程可能在不同的CPU上运行,一个CPU可能需要较长的内存提取,而另一个CPU只需很短的内存提取,因此它比另一个慢。 同样,准备代码的机器指令的长度也可能不同。 通常,您根本不知道幕后发生的事情,Java也不保证线程运行的顺序。
  2. 在这种特殊情况下,不太可能会重新安排指令,因为方法很短。 同样,您无法确定发生了什么,因为它取决于特定的JVM,CPU数量,CPU类型,调度程序和内存安排-您无法保证。

将同步添加到函数setValue/getValue意味着,要执行该代码段的任何线程都必须首先获取(或等待)该对象的锁。

如果我们假设在线程A调用setValue / getValue之前没有持有任何锁,则线程A将立即获得该锁。 但是,在此期间,如果线程B调用setValue/getValue ,则它必须等待线程A放弃锁才能执行该方法。

但是,如果两个线程都在等待对象的锁定,则我们不能保证操作系统会首先选择哪个线程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM