【问题标题】:If volatile fields are read directly from memory, where are non-volatile fields read from?如果直接从内存中读取易失性字段,非易失性字段从哪里读取?
【发布时间】:2019-09-28 12:19:48
【问题描述】:

我最近在阅读 volatile 字段是线程安全的,因为

当我们对变量使用 volatile 关键字时,所有线程都会读取 它的值直接从内存中获取,不缓存

所以我想知道,对于非易失性字段,线程会从哪里读取其值?我以为一切都存储在内存中。

【问题讨论】:

  • 寄存器不是直接的“内存”,所以有些东西不是内存,可以放置变量。
  • 可以将非易失性字段(例如“private int x”)存储在寄存器中吗?如果是,那是如何确定的?
  • 每个static 变量和每个成员变量在主内存中都有一个位置,但是多处理器系统中的每个CPU 都可以保留自己的本地副本。这就是下面回答的“缓存”。 “同步”系统中不同 CPU 的缓存是昂贵的。因此,Java 语言不会这样做,除非在程序员知道在另一个线程中需要一个线程更改的值的特殊点。当您将变量声明为volatile 时,您是在告诉编译器您需要对变量的每次访问进行同步。
  • 嗨@Solomon,所以我假设同步不同缓存的 CPU 可能很昂贵,但不像下面的答案之一所暗示的那样直接从内存访问东西那么昂贵?跨度>
  • @Dave,在大多数多处理器架构中,没有“直接访问事物”之类的东西。 所有 访问都经过缓存。 en.wikipedia.org/wiki/Cache_(computing)

标签: java multithreading memory volatile


【解决方案1】:

处理器上的核心/线程可以获取非volatile 变量的副本并将其放置在自己的缓存中(不是主内存)。这个缓存的值会被更新,但主内存版本不会直到线程认为是时候刷新值了。

如果另一个内核/线程尝试从主内存访问该值,它可能会在不知不觉中找到一个过时的值。

volatile 修饰符意味着变量数据每次更改时都会在主内存中保持最新,因此随时准备好其他线程需要访问/编辑它。

【讨论】:

  • JVM 是否可以控制这些变量的存储位置?我认为这些是传递给操作系统的指令,然后操作系统决定在哪里存储东西。
  • 据我所知,这并不多,我认为 JVM 使用本机库告诉操作系统变量是“易失的”。该术语在该级别有所不同。
  • are only periodically updated in main memory 您能否提供有关该时期的更多详细信息?频率是多少?
  • 一个线程将获取数据并存储本地(缓存)副本并仅对其进行操作。一旦线程处理完变量,它会将更新的值刷新回主内存。此时,另一个线程可能会尝试访问数据,但在不知不觉中发现它已过期。根据我所知道的,值没有固定的更新周期。
【解决方案2】:

每个线程都有一个主内存缓存。

一个字段可能被声明为 volatile,在这种情况下,Java 内存模型 确保所有线程看到变量的一致值

例如,我们有这个不是线程安全的静态计数器

static int counter = 0;

如果两个线程读写这个变量会是这样的

Main memory  - > static int counter 
T1-> Cache of the Main Memory. Reads/Writes directly from Cache
T2-> Cache of the Main Memory. Reads/Writes directly from Cache 

所以读写时会不一致。

static volatile int counter = 0;

Main Memory - > static int counter;
T1 - > Read and Writes directly from the memory
T2 - > Read and Writes directly from the memory

我希望我给你一个简单的总结。因为您需要检查更多关于并发性和Atomic Access 变量的并发性 在Java Docs中查看更多信息

【讨论】:

    【解决方案3】:

    您引用的陈述是一种误解。 我推荐以下文章:https://software.rajivprab.com/2018/04/29/myths-programmers-believe-about-cpu-caches/

    如果 volatile 变量每次都真正从主内存写入/读取,它们会非常慢 - 主内存引用比 L1 缓存引用慢 200 倍。实际上,volatile-reads(在 Java 中)通常可以像 L1 缓存引用一样便宜,从而打破 volatile 强制读取/写入一直到主内存的概念。如果您因为性能问题一直避免使用 volatile,那么您可能已经成为上述误解的受害者。

    【讨论】:

    • 上述文章似乎暗示甚至可以从 L1 缓存中读取易失性字段。真的吗?那是线程安全的吗?
    • @Dave,在典型的多处理器系统上(并非所有硬件平台都以完全相同的方式工作)每个内存访问都必须通过缓存。通常平台有特殊的指令可以用来“同步”属于不同处理器的缓存。 volatile 的一个作用是,每次访问 volatile 变量时,它都会强制 JVM 执行那些特殊的同步操作代码。
    猜你喜欢
    • 2018-01-18
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    • 1970-01-01
    • 2016-04-04
    相关资源
    最近更新 更多