Java 为什么';t JVM compile“;递增一个整型变量";作为原子获取和增量操作?

Java 为什么';t JVM compile“;递增一个整型变量";作为原子获取和增量操作?,java,multithreading,jvm,Java,Multithreading,Jvm,我了解到在Java中递增int变量不是一个原子操作,但是,我发现CPU支持原子获取和递增操作 所以我的问题是,为什么JVM不编译递增int变量操作到CPU支持的原子获取和递增操作,这在多线程编程中可能很有用 早期的处理器有原子测试和设置、获取和增量,或者交换指令,这些指令足以实现互斥锁,而互斥锁又可以用来实现更复杂的并发对象 --Java并发在实践中的应用 --我想并不是每一台可以运行Java的机器都有这样的原子操作。请记住,Java可以在多个不同的平台和许多不同的设备上运行-- 检查一位同事在

我了解到在Java中递增int变量不是一个原子操作,但是,我发现CPU支持原子
获取和递增
操作

所以我的问题是,为什么JVM不编译
递增int变量
操作到CPU支持的原子
获取和递增
操作,这在多线程编程中可能很有用

早期的处理器有原子测试和设置、
获取和增量
,或者交换指令,这些指令足以实现互斥锁,而互斥锁又可以用来实现更复杂的并发对象

--Java并发在实践中的应用

--我想并不是每一台可以运行Java的机器都有这样的原子操作。请记住,Java可以在多个不同的平台和许多不同的设备上运行--


检查一位同事在评论中提到的答案,因为对于多核处理器,每个核或多处理器系统中的每个处理器都有单独的程序内存缓存。为了运行得更快,程序被加载到此缓存中,因为它的运行速度比RAM快得多,但随后每个内核将一个线程执行到自己的RAM内存副本(在缓存中),其他线程通常不可见

这个内存当然可以与ram同步,但是这个操作很慢。直接从RAM读写速度很慢,这是多cpu/核心系统的瓶颈,这就是为什么在处理器中有一个高速缓存,将一段程序预加载到它,这样它可以运行得更快(512KB的程序通常包含很多循环,因此它会执行一段时间,而在这段时间内,其他内核从RAM中馈送,它会加速整个系统)

使一个操作原子化并对所有线程可见意味着它不能被缓存,因此它需要使用直接RAM内存进行读写(或等效的——一些特殊的缓存),这当然会降低应用程序的速度。这就是它不是默认值的原因,因为通常情况下,您不需要潜在的慢速同步。

因为:

  • Java编译器不编译成机器码,而是编译成字节码,然后

  • JVM字节码中没有原子获取和增量指令,除了局部变量

  • 因为Java标准(JLS)不需要它,而且它是一个昂贵的操作,应该只在需要时使用

    为什么JVM不将递增int变量操作编译为CPU支持的原子获取和递增操作,这在多线程编程中可能很有用

    我曾经多次问过这个问题,因为我认为当这个字段是
    易变的
    。我得到的反馈是,由于Java的早期版本没有这样做来修复它,现在会破坏向后兼容性。也就是说,有些人可能会有一个程序在不知不觉中依赖于这种行为。考虑到我没有感觉到这一点非常有用,也不能保证增量不会以原子方式运行,99%以上的时间都是这样。我认为100%的时间(尤其是字段不稳定时)没有问题,但这只是我的观点

    所以我的问题是,为什么JVM不将递增的int变量操作编译成CPU支持的原子获取和递增操作,这在多线程编程中可能很有用

    因为在典型的现代CPU上,原子读-修改-写操作(如递增)比其相应的非原子操作昂贵几十倍。而且它不会提供任何好处——代码不能依赖原子操作,因为它们不能保证是原子操作。那么好处是什么呢

    虽然这与您的问题没有直接关系,但因为其他许多人都错误地解释了这一点,我将解释原子增量和非原子增量之间的两个区别(在硬件级别):

  • 原子增量不能与同一内核中的某些其他操作重叠。也就是说,它必须在某个特定的时间发生。这意味着CPU指令流水线通常会受到原子操作的严重负面影响

  • > p>防止另一线程在原子操作中间(在读和写之间)将操作重叠到同一高速缓存行,缓存线在原子操作期间被锁定。如果另一个内核试图从执行原子操作的CPU获取缓存线,即使是非原子操作,它也必须等待原子操作完成。(这过去是总线锁定。现代CPU更智能。)

    当然,并不能保证每个CPU都是相同的,但是具有多核的现代CPU和流行的Java实现几乎可以肯定具有高度优化的多核操作。当然,未来的CPU可能会更好

    此外,为了纠正另一个常见的误解:现代多核CPU上的缓存直接通信。它们永远不需要通过主存来同步CPU(除非极少数情况下,所需的数据仅在主存中,并且由于某种原因无法预取)。若数据在一个内核的缓存中,它可以使用的变体直接进入另一个内核的缓存。这是一件好事——若内核间同步必须通过RAM,多核CPU的性能会非常差

    所以我的问题是,为什么JVM不编译递增的int变量 对原子获取的操作和CPU执行的增量操作 支持,这在多线程编程中可能很有用

    除了JVM可能需要针对缺乏此类本机指令的硬件这一显而易见的答案之外,我还想讨论更一般的问题,“为什么不让每个基本操作原子化,即使所有目标操作都很难实现呢?”