简体   繁体   中英

Does Java also guarantee that all variable changes before synchronized will be visible to the next thread which synchronizes on same object?

In the below code will Java ensure that latest copy of a is visible to the thread which calls getAB() ?

I understand that the values returned by getAB() might not be the same as set by setAB , but will Java ensure that the value of a seen by other thread will always either be more recent or consistent with the value of b ?

public class Pair {

    private int a;
    private int b;

    public Pair() {
        super();
    }

    public void setAB(int a, int b) {
        this.a = a;
        synchronized (this) {
            this.b = b;
        }
    }

    public int[] getAB() {
        int[] arr = new int[2];
        arr[0] = a;
        synchronized (this) {
            arr[1] = b;
        }
        return arr;
    }

}

Followup question : what happens if we move the arr[0] = a; statement to after the synchronized block?

Like this ...

public int[] getAB() {
    int[] arr = new int[2];
    synchronized (this) {
        arr[1] = b;
    }
    arr[0] = a;
    return arr;
}

Does java also guarantee that all variable changes before synchronized will be visible to the next thread which synchronizes on same object?

Yes it does ... assuming the mutex has been acquired by the next thread.

However. In your example getAB is using a before it has synchronized on this , so arr[0] = a could assign a stale value.

Assuming that setAB is called before getAB , the happens before relationships in your example will be:

      this.a = a;  
  HB  this.b = b;  
  HB  released mutex in first thread
  HB  acquired mutex in second thread
  HB  arr[1] = b;

and separately

      arr[0] = a;
  HB  arr[1] = b;

From this we can deduce that

  this.b = b;  HB  arr[1] = b;

but we cannot deduce that

  this.a = a;  HB  arr[0] = a;   // FALSE!

Then you move arr[0] = a; to after the synchronized block, the updated value of a is now guaranteed to be visible.

I won't reproduce the full "happens before analysis", but now we get

  arr[1] = b;  HB  arr[0] = a;

and we can deduce that

  this.a = a;  HB  arr[0] = a;   // NOW TRUE!

Having said this, it is generally NOT recommended that you write code that relies on careful analysis of happens before relationships etcetera. It is better to use the higher level synchronization classes, or (as in this case) just do all of the reading and writing of shared variables inside the synchronized block.

(Generally speaking, the performance benefit you get from clever implementations of concurrency is usually tiny compared to overall application performance. And the long term cost of finding and fixing Heisenbugs caused by subtle synchronization code flaws can be huge.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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