5

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.
Vladimir Vagaytsev
  • 2,871
  • 9
  • 33
  • 36
sunny
  • 213
  • 1
  • 2
  • 9
  • 3
    There is a difference between "thread safe" and "predictable". Adding `synchronized` will mean that Thread B will always see the _correct_ `value`, but it might not yet have been updated. As you point out, there is no guarantee that the two threads will execute in the assumed order. Without `synchronized` there is no _visibility guarantee_, so even if A executed first, B might still see the old `value`. – Boris the Spider Aug 20 '16 at 08:14
  • I think you are misreading. If there is a `synchronized` keyword it means the JVM will guarantee that at any given moment you can't have two threads executing both `setValue` and `getValue` at the same time (one will have to complete their method execution before the other thread can enter the other method). It does *not* guarantee anything about the order of those operations. – Paolo Aug 20 '16 at 08:16
  • 1
    To ensure "correct" visibility you should declare `private volatile int value = 0;`. This make shure that a value written is seen by all other threads after. – PeterMmm Aug 20 '16 at 08:17
  • 1
    @PeterMmm No, using `synchronized` is just fine. No need to add volatile. – Erwin Bolwidt Aug 20 '16 at 08:19
  • @ Erwin Bolwidt correct, in this case for an int. I prefer `volatile` anyway, because it makes clear the intention/need to make the value visible to all other threads ... even we get it wrong in another place (very usual in multi-threaded programming). – PeterMmm Aug 20 '16 at 08:27
  • @PeterMmm No difference if it were a `long` or `double`. `synchronized` and `volatile` can both establish happens-before relationships. A write to a long in one thread, synchronized on monitor A, read by another thread that has synchronized on monitor A as well, will always see an atomic update of all 64 bits. – Erwin Bolwidt Aug 20 '16 at 08:30
  • @PeterMmm randomly scattering threading constructs in your code to make "_clear the intention/need_" is about as helpful as a hole in the head. – Boris the Spider Aug 20 '16 at 08:30
  • 1
    @ErwinBolwidt you sure that's true? I seem to recall that the JVM only guarantees atomic assignment of references and 32bit types, so `long` and `double` might happen in two assignments, especially on 32bit architecture - [this SO answer](http://stackoverflow.com/a/4756578/2071828) seems to agree. I too lazy to check the Java 8 JLS... – Boris the Spider Aug 20 '16 at 08:32
  • @Erwin Bolwidt as long for `long` I read [this](https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html) and I understand `long` is diferent to handle, but maybe I'm wrong. – PeterMmm Aug 20 '16 at 08:34
  • @Boris the Spider OT I won't survive without my hole(s) in the head, at least for breathing. You should be something special, concerning this. – PeterMmm Aug 20 '16 at 08:37
  • @ErwinBolwidt What's incorrect? *"..it is not possible for two invocations of synchronized methods on the same object to interleave"* and *"..when a synchronized method exits [it] guarantees that changes to the state of the object are visible to all threads."* https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html But that doesn't stop me calling getValue before setValue. – Paolo Aug 20 '16 at 08:37
  • @BoristheSpider If it were not in a synchronized block, then on some architectures it would be possible to see (as a data race) only 32 bits of a 64 bit unit updated. But as the synchronized block is around it, and the reader also synchronized on the same monitor, then this issue doesn't exist (the visibility guarantees resulting from the happens-before relationship always guarantee that the data is completely visible) – Erwin Bolwidt Aug 20 '16 at 08:48
  • @ErwinBolwidt yes, `synchronized` would of course work. I just misread your comment as suggesting that `volatile` provides the same visibility guarantees, which isn't entirely true - apologies. – Boris the Spider Aug 20 '16 at 08:49
  • @Paolo I misread what you said about ordering. I'll remove my comment – Erwin Bolwidt Aug 20 '16 at 08:49
  • 1
    @BoristheSpider volatile also guarantees atomic updates to 64-bit quantities: https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7 – Erwin Bolwidt Aug 20 '16 at 08:52
  • @Paolo yes , so if we want to guatantee function **setValue** executed before **getValue**,we must do something else, like using **Semaphore** in java – sunny Aug 21 '16 at 10:42
  • @ErwinBolwidt yes , Jvm Specifications allow jvm implemention have a not atomic treatment of double and long variables, but most jvm can ensure atomic of double and long although we not use volatile key word – sunny Aug 21 '16 at 10:55
  • @sunny I never said anything different. But you need to write Java apps not to "I think that most JVMs" - but to the specification. – Erwin Bolwidt Aug 21 '16 at 13:06

2 Answers2

9

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.
RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • This is the real issue, everyone else is completely missing the point. – Boris the Spider Aug 20 '16 at 08:22
  • Thanks very much @RealSkeptic. But I must to ensure a question fudrther. Seen below code – sunny Aug 21 '16 at 10:04
  • @sunny Don't try to post code in comments. Edit your question, add your additional problem, if it is part of the original one, and only put a comment to draw my (it anybody else's) attention to your edit. – RealSkeptic Aug 21 '16 at 10:07
  • @RealSkeptic thanks very mush,I edit the question again. I must to ensure some questions futher. can answer my two small questions below. Thanks – sunny Aug 21 '16 at 10:34
  • @sunny I added an answer to those two questions. – RealSkeptic Aug 21 '16 at 10:47
  • @RealSkeptic thanks very much, you solved my questions. so if we want to let function **setValue()" run before "getValue()", we must use else synchronized mechanism like **Semphere** or **Lock** to ensure that – sunny Aug 21 '16 at 11:02
0

Adding synchronized to the functions setValue/getValue implies that any thread that wants to execute that piece of code will first have to obtain (or wait) for a lock on that object.

If we assume no locks being held before Thread A calls setValue/getValue, Thread A will immediately get the lock. However, in the interim, if thread B calls setValue/getValue, it will have to wait for Thread A to relinquish the lock before it can execute the method.

However, had both the threads been waiting for a lock on the object, we cannot guarantee which one would have been first picked by the OS.

Dici
  • 25,226
  • 7
  • 41
  • 82