1

Jack ShiraziJava Performance Tuning中写道:

这意味着变量的访问和更新是自动同步的(只要它们不是长整数或双精度数)。如果一个方法只包含一个变量访问或赋值,就没有必要为了线程安全而使其同步,并且出于性能考虑不这样做的所有理由。线程安全进一步扩展到独立于任何其他变量值访问或分配给变量的任何语句集。

根据上面的描述,操作 likeflag = true总是原子的,不需要synchronize.

但是,这里有另一篇文章将以下情况视为数据竞争


class DataRaceExample {
  static boolean flag = false;//w0
  static void raiseFlag() {
    flag = true;//w1
  }
  public static void main(String... args) {
    ForkJoinPool.commonPool().execute(DataRaceExample::raiseFlag);
    while (!flag);//r_i, where i ∈ [1, k), k may be infinite
    System.out.print(flag);//r
  }
}

作者说:

现在,所有执行都有数据竞争,因为标志不是易失的

这两篇文章之间的冲突让我很困惑。

4

3 回答 3

3

杰克·希拉兹错了。

原始变量的访问和更新,例如int原子的,但不是同步的。

因为它是原子的,所以可以通过使其成为完全线程安全的volatile。否则,在不同内核上运行的其他线程可能会看到陈旧的值,因为CPU 缓存尚未刷新。

于 2019-12-29T06:32:55.310 回答
1

在您的第二个示例中,循环将不会运行或永远运行。

这样做的原因是变量标志在第一次检查时只被读取一次。

如果标志是易失性的,则每次都从内存中读取。这允许另一个线程更改标志的值并且循环将看到它。

于 2019-12-29T04:16:53.963 回答
1

Jack Shirazi 试图指出的一点是,对原始类型以外的非易失性访问,double并且long保证根据 JMM 以原子方式执行。因此,同步对于防止例如在存在并发访问时的读取和写入撕裂是不必要的。

之所以出现这种混乱,是因为他的书早于 JSR-133,并且他使用了诸如“自动同步”之类的术语,这与 JMM 中的现代同步概念不符。

于 2019-12-29T21:26:39.997 回答