内存不一致与线程交错有何不同?

2024-05-11

我正在编写一个多线程程序,正在研究是否应该使用volatile对于我的布尔标志。关于并发性的文档 oracle Trail 没有解释任何关于memory consistency errors以外:

当不同的线程有内存一致性错误时,就会发生内存一致性错误。 对相同数据的看法不一致。

假设这些不一致的观点仅在“写入”操作之后发生。但多久之后呢?

实施例1

Thread A: Retrieve flag.
Thread B: Retrieve flag.
Thread A: Negate retrieved value; result is true.
Thread A: Store result in flag; flag is now true.
Thread B: System.out.print(flag) --> false

Since Thread A and Thread B同时运行,打印结果也可能为 true,具体取决于检索时间flag。对于不一致来说,这是完全有道理的。

但方式memory consistency errors描述了(对变量的写入不一定反映在其他线程中)听起来这也是正确的:

实施例2

Thread A: Retrieve flag.
Thread A: Change retrieved value; result is true.
Thread A: Store result in flag; flag is now true.
//any longer amount of time passes (while Thread A didn't explicitly happen-before Thread B, it obviously did.)
Thread B: Retrieve flag.
Thread B: System.out.print(flag) --> true OR false (unpredictable)

我强烈认为示例 2 不可能为真。问题是,只有当它is是的,我可以看到使用volatile建立一个happens-before.

如果是真的,为什么会这样呢?如果不是..为什么要使用volatile根本吗?


严格来说,理解 JVM 内存模型最具挑战性的事情之一是:timing(即你的挂钟)完全无关。

不管how long(根据您的挂钟)如果没有,则两个单独线程中的两个操作之间经过的时间发生在之前关系,绝对不能保证每个线程将在内存中看到什么。


In your 实施例2,棘手的部分是你提到的,

虽然线程 A 没有明确发生在线程 B 之前,显然是这样.

从上面的描述来看,你唯一能说的是obvious就是根据你挂钟的时间测量,发生了一些操作later相对于其它的. But 这并不意味着发生在严格意义上的 JVM 内存模型的关系。


让我展示一组与您对上面示例 2 的描述兼容的操作(即,根据您的挂钟进行的测量),这可能会导致true or false,并且不能做出任何保证。

  • 主线程M启动线程A和线程B: 有一个发生在关系之前线程 M 和线程 A 之间以及线程 M 和线程 B 之间。因此,如果没有发生其他情况,线程 A 和线程 B 都将看到与线程 M 相同的布尔值。我们假设它被初始化为false(以使其与您的描述兼容)。

假设您正在多核计算机上运行。另外,假设线程 A 分配在 Core 1 中,线程 B 分配在 Core 2 中。

  • 线程A读取布尔值: 它必然会读取false(请参阅前面的要点)。当这个读取发生时,它might某些内存页(包括包含该布尔值的内存页)将被缓存到 Core 1 的 L1 缓存或 L2 缓存(该特定核心本地的任何缓存)中。

  • 线程A取反并存储布尔值: 它将存储true现在。但问题是:在哪里?直到一个发生在之前发生这种情况时,线程 A 可以自由地将这个新值仅存储在运行该线程的 Core 的本地缓存中。因此,该值可能会在 Core 1 的 L1/L2 缓存中更新,但在处理器的 L3 缓存或 RAM 中保持不变。

  • 经过一些time(根据你的挂钟),线程B读取布尔值:如果线程 A 没有将更改刷新到 L3 或 RAM,则线程 B 完全有可能会读取false。另一方面,如果线程 A 刷新了更改,那么它是可能的线程 B 将读取true(但仍然不能保证——线程 B 可能已经收到了线程 M 的内存视图的副本,并且由于缺少发生之前,它不会再次访问 RAM 并且仍然会看到原始值)。

唯一的方法是保证任何事情都要有一个明确的发生在之前:它将强制线程 A 刷新其内存,并强制线程 B 不从本地缓存读取,而是真正从“权威”源读取。

如果没有发生之前,正如您从上面的示例中看到的那样,任何事情都可能发生,无论多少时间(从您的角度来看)不同线程中的事件之间经过。


现在,最大的问题是:为什么会volatile解决示例 2 中的问题?

如果该布尔变量被标记为volatile,如果操作的交错按照上面的示例 2 发生(即,从挂钟的角度来看),那么只有这样,线程 B 才能保证看到true(即,否则根本没有任何保证)。

原因是volatile帮助建立事前发生的关系。其过程如下:写到一个volatile变量发生在对同一变量的任何后续读取之前.

因此,通过标记变量volatile,如果从计时角度来看,线程 B 仅在线程 A 更新后才读取,则保证线程 B 能够看到更新(从内存一致性角度来看)。

现在有一个非常有趣的事实:如果线程 A 对非易失性变量进行更改,然后更新易失性变量,然后(从挂钟的角度来看)线程 B 读取该易失性变量,也保证线程 B 会看到所有更改到非易失性变量!这是由非常复杂的代码使用的,这些代码想要避免锁并且仍然需要强大的内存一致性语义。它通常被称为挥发性变量捎带.


最后一点,如果您尝试simulate(缺乏)发生在关系之前,这可能会令人沮丧......当您将内容写到控制台时(即,System.out.println),JVM 可能会在多个不同线程之间进行大量同步,因此大量内存实际上可能会被刷新,并且您不一定能够看到您正在寻找的效果...很难模拟所有这!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

内存不一致与线程交错有何不同? 的相关文章

随机推荐

  • Java-忽略字符串中的分隔符

    我有一个使用管道符号 作为分隔符的字符串 但是 字符串数据也contains管道符号 有没有办法忽略这个 Example name address age John 123 Wood Road Street London 25 因此 当我这
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • 使用 C# 将 DateTime 转换为字符串的魔术字符串

    今天我遇到了一个讨厌的错误 该任务非常简单 我所需要做的就是将 DateTime 对象转换为字符串 yyyymmdd 格式 yyyymmdd 部分在外部软件供应商的开发文档中进行了说明 因此 我方便地从他们的文件中复制了字符串并粘贴到我的代
  • 从 iframe 访问 Session 变量

    我有一个 jsp 我在其中设置了会话变量 但是 当我尝试读取另一个 jsp 中的会话变量 user 时 该变量已加载到 iframe 同一主机 服务器等 中 然后我得到 NullPointerException 如何在 iframe 中获取
  • C# - 挂钩现有 COM 对象

    假设我们有一个现有进程 或应用程序 它从 ocx 文件 例如 MyCOMLibrary ocx 调用 COM 对象 有没有办法编写一个 C 库来精确复制 ocx 文件 这样原始应用程序就可以调用您的 C 代码而不是原始 COM 对象 当然
  • 如何在 Jersey 容器中配置欢迎文件 (HTML/JSP)

    我有一个 Jersey RESTful Web 服务项目 我已经在中配置了 Jersey 容器web xml一切正常 在同一个项目中 我引入了一个 HTML 页面并包含在
  • 显示 Facebook 墙上我网站上的帖子

    我正在建立我的网站 我想做的事情如下 我想在我的网页上展示我最近在 Facebook 墙上发布的帖子 但只是我自己的帖子 不是其他人都只是我的 Here s an example of how I would like to do it 我
  • Solidity 中的地址(0)是什么

    谁能向我解释一下什么address 0 是在 Solidity 中吗 我在文档中找到了以下内容 但对我来说并没有真正意义 如果目标账户是零账户 地址为0的账户 则交易创建一个新合约 如前所述 该合约的地址不是零地址 而是从发送方及其发送的交
  • Python 仪器驱动程序

    我是一名实验物理学家 并且是Python http en wikipedia org wiki Python 28programming language 29 我发现它非常适合数据分析和脚本编写 实际上我还用它来连接实验室仪器 网络分析仪
  • 如何使用应用程序脚本在 Google 电子表格中移动工作表 [重复]

    这个问题在这里已经有答案了 我想使用应用程序脚本在电子表格中移动工作表 怎么做 最好的直播 你看过吗文档 https developers google com apps script reference spreadsheet sprea
  • 使用查询选择器从 VBA 中抓取

    我使用了该网站的代码来提取数据site https bazashifer ru proflist profnastil Option Explicit Public Sub GetInfo Dim sResponse As String i
  • 使用 Mail_Mime 发送附件到 GMail,收到“noname”附件

    我有一个非常简单的网站表单 可以包含附件 它使用 gmail 的 smtp 发送到 gmail 地址 一切工作都很好 除了文件以 noname 形式到达 没有文件名或扩展名 如果您下载附件并使用正确的文件名重命名它 则该文件可以正常打开 我
  • 输入字符串的格式不正确

    下面的代码产生一个错误 指出我的输入字符串的格式不正确 为什么 private void button7 Click object sender EventArgs e string uriAddTagtoGroup string Form
  • 使用 fread 导入数据后所有列均作为字符

    我导入了一个 CSV 文件 包含文本列和数字列 x lt fread myfile csv header TRUE verbose T na strings c null null 但导入后 当我运行summary x 时 所有列都被视为字
  • 如何知道生成的序列最多是一定长度

    我想知道生成的序列是否少于 2 个条目 gt gt gt def sequence for i in xrange secret yield i 我的低效方法是创建一个列表 并测量其长度 gt gt gt secret 5 gt gt gt
  • 使用 PassportJS 和 Connect for NodeJS 对 Facebook 用户进行身份验证

    我正在尝试使用 connect 将 Passport 集成到我的 NodeJS 服务器中 但似乎无法正确执行 所有指南 示例都使用expressJS 因此我尽力重新格式化代码以与我的代码一起使用 但我似乎无法让它工作 相关部分写在下面 有人
  • 如何在chartjs中绘制多个时间序列,其中每个时间序列都有不同的时间

    例如 我有两个时间序列 s1 2017 01 06 18 39 30 100 2017 01 07 18 39 28 101 and s2 2017 01 07 18 00 00 90 2017 01 08 18 00 00 105 我想在
  • 如何从 Java 中的 String 获取类引用?

    如果我需要返回一个基于字符串实例化的类 例如 从 JSON 创建逻辑 应该如何编写 我最初的想法是一个 switch 但在 Android 框架的 Java 版本中 看起来 switch 语句不允许使用字符串 接下来的想法是 HashMap
  • iOS - 检测应用程序是否正在从 Xcode 运行 [重复]

    这个问题在这里已经有答案了 我试图根据代码是否通过 USB Xcode 调试 运行或在从应用程序商店下载的生产模式 发布 运行来启用 禁用部分代码 我知道检查它是否正在运行DEBUG or RELEASE像这样的模式 ifdef DEBUG
  • 内存不一致与线程交错有何不同?

    我正在编写一个多线程程序 正在研究是否应该使用volatile对于我的布尔标志 关于并发性的文档 oracle Trail 没有解释任何关于memory consistency errors以外 当不同的线程有内存一致性错误时 就会发生内存