0

在下面的代码中,Java 是否会确保a调用的线程可以看到最新的副本getAB()

我知道返回的值getAB()可能与设置的不同setAB,但Java会确保a其他线程看到的值总是更新或与值一致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;
    }

}

后续问题:如果我们将arr[0] = a;语句移到块之后会发生什么synchronized

像这样 ...

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

1 回答 1

2

java是否还保证同步之前的所有变量更改对于在同一对象上同步的下一个线程都是可见的?

是的……假设互斥体已被下一个线程获取。

然而。在您的示例getAB中,它在同步a 之前this使用,因此arr[0] = a可以分配一个陈旧的值。

假设setAB调用 before getAB,您的示例中的发生之前的关系将是:

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

并分别

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

由此我们可以推断

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

但我们无法推断

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

然后你移动arr[0] = a;synchronized块之后,现在保证更新的值a是可见的。

我不会重现完整的“在分析之前发生”,但现在我们得到了

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

我们可以推断出

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

话虽如此,通常不建议您编写依赖于仔细分析之前发生的关系等的代码。最好使用更高级别的同步类,或者(在这种情况下)只在synchronized块内执行所有共享变量的读取和写入。

(一般来说,与整体应用程序性能相比,您从巧妙的并发实现中获得的性能优势通常很小。而且发现和修复由细微的同步代码缺陷引起的 Heisenbugs 的长期成本可能是巨大的。)

于 2021-06-20T10:51:52.623 回答