为什么Java中的引用赋值是原子的?

Geo*_*rge 9 java concurrency jvm scala atomic

据我所知,引用赋值在64位JVM中是原子的.现在,我假设jvm不在内部使用原子指针对此进行建模,因为否则就不需要原子引用.所以我的问题是:

是java/Scala的"规格"中的原子引用分配并保证会发生,或者它是一个很幸运的巧合,大多数时候都是这样吗?

对于编译为JVM字节码的任何语言(例如clojure,Groovy,JRuby,JPython等),是否隐含了原子引用赋值?

如果不在内部使用原子指针,引用赋值如何是原子的?

Hol*_*ger 13

首先,引用赋值是原子的,因为规范是这样说的.除此之外,JVM实现者没有障碍来实现此约束,因为64位引用通常仅用于64位体系结构,其中原子64位分配是免费的.

您的主要困惑源于这样的假设:由于其名称,额外的"原子参考"功能意味着这一点.但是这个AtomicReference类提供了更多,因为它封装了一个volatile引用,它在多线程执行中具有更强的内存可见性保证.

具有原子引用更新并不一定意味着读取引用的线程也将看到关于通过该引用可到达的对象的字段的一致值.它保证的是,您将读取null某个线程实际存储的现有对象的引用或有效引用.如果您需要更多保证,则需要构造,如同步,volatile引用或AtomicReference.

AtomicReference还提供像或的原子更新操作.普通参考变量不可能实现这些.compareAndSetgetAndSet

  • “发布”意味着分配给其他线程可见的变量。不,通过普通引用发布时,无法保证以前更新的可见性。如果你写 `point=new Point(42,100)` 并且 `point` 既不是 `final` 也不是 `volatile`,其他线程 *可能* 会看到新的引用,同时仍然会看到其中任何一个的默认值 (`0`), “x”或“y”,或两者(在没有其他同步的情况下)。 (3认同)
  • 从“AtomicReference”的意义上来说,“原子指针”不一定是“原子引用”。因此,有必要指出引用(即底层指针)*是*原子的,但仍然与“AtomicReference”的功能不匹配。 (2认同)
  • 这与较旧的实例无关;在赋值之前,引用可能是“null”,这无关紧要。问题是,`new Point(42,100)` 不是原子操作。它创建一个新的 `Point` 实例,所有字段都为其默认值(`0` 表示 `int`),然后,将执行构造函数,将值 `42` 分配给 `x` 和 `100`到`y`,然后引用被分配给`point`,*从单个线程的角度来看*。一个不同的线程可能会看到这些操作乱序,看到对新`Point` 实例的引用而没有看到赋值的效果。 (2认同)
  • 这不适用于不可变对象。如果你将 `x` 和 `y` 声明为 `final`,它们就不会受到这种数据竞争的影响。或者,您可以将 `point` 声明为 `final`,但这意味着整个操作是构造函数或初始值设定项的一部分。或者,好吧,将 `point` 声明为 `volatile` 可确保其他线程在通过 `point` 引用查看新的 `Point` 实例时看到所有先前的更新(`x=42; y=100;`)。 (2认同)

Ole*_*.V. 6

原子参考分配在规格中.

对引用的写入和读取始终是原子的,无论它们是实现为32位还是64位值.

引用自JSR-133:Java(TM)内存模型和线程规范,第12节非原子处理doublelong,http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf.

  • 链接的PDF描述了JSR133提出的Java 5的内存模型.为了证明它确实进入了Java 5 JLS并且现在仍然存在,最好链接到[JLS§17.7](https://docs.oracle.com/javase/specs/jls/se8/html/jls -17.html#JLS-17.7). (2认同)