Search code examples
javamultithreadinghappens-before

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


I am reading 《Understanding the JVM Advanced Features and Best practices》 that has a code segment that explains happens-before rule in java. I cannot understand. The code is below:

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;
}

Suppose that thread A starts before thread B in code. I can understand that we don't know the result returned by getValue() in Thread B, because it is not thread safe. But the book says if add synchronized key word to function setValue() and getValue(), then there is not exists thread safe problem and method getValue() will return the right value. The book explains that because synchronized meets with happens-before rule. So I have two questions by below code.

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. Although A.start() run before B.start() and value is volatile, we can't ensure thread B can print out 10, right? Because thread B is possible scheduled first by JVM, then thread B will print 0 not 10.
  2. Even if thread A scheduled before thread B by JVM, but we also can't guarantee that the instruction this.value = value executed by JVM before return this.value because JVM will sort instructions again. Am I understanding is right? Please help me.

Solution

  • The issue of "happened before" is not that it causes thread A to set the value before thread B. It is that although it may happen that thread A got to the this.value = value chronologically before thread B got to run getValue, the value B sees may still be the old value.

    That is, in a threaded environment, even if two instructions are performed in chronological order, it doesn't mean that the results of one will be seen by the other.

    If thread B happened to call the method first, it will always get the old value. But if it happened to call the method second, it's unknown if it gets the old or the new value.

    For this reason, you have to use means to ensure the "happens before" rule, and then you know that the results of what "happened before" are seen by what "happens after".

    So if value is volatile, for example, it ensures that if setValue() is called by thread A before thread B, then thread B will see the new value.

    ╔═════════════════════╤════════════════════════╤═════════════════════╗
    ║ 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                   ║
    ╚═════════════════════╧════════════════════════╧═════════════════════╝
    

    Regarding your two questions:

    1. True. You can't know which of them gets to that instruction first. It's not just the issue of which thread is scheduled first. The threads may be running on different CPUs, one CPU may need a long memory fetch, the other only a short memory fetch, so it is slower than the other. Also, it may be that the machine instructions in preparation for the code are of different length. In general, you simply don't know what is happening behind the scenes, and Java makes no guarantees about the order the threads run.
    2. It's unlikely that instructions will be re-arranged in this particular case, because the methods are very short. Again, you can't tell what is happening because it is up to the particular JVM, number of CPUs, type of CPU, scheduler and memory arrangement - you have no guarantees.