并发访问公共字段。为什么可以观察到不一致的状态?

2024-01-22

我正在阅读 B. Goetz Java 并发实践,现在我正在阅读section 3.5关于安全出版。他说:

// Unsafe publication
public Holder holder;
public void initialize() {
    holder = new Holder(42);
}

这种不正确的发布可能会允许另一个线程观察 部分构造的对象。

我不明白为什么可以观察部分构造的子对象。假设构造函数Holder(int)不允许this逃离。因此,构造的引用只能由调用者观察到。现在,作为JLS 17.7 https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 stated:

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

线程不可能观察部分构造的对象。

我哪里错了?


因此,构造的引用只能由调用者观察到。

这就是你的逻辑崩溃的地方,尽管这似乎是完全合理的说法。

首先要做的事情是:17.7 提到的原子性仅表示当您读取引用时,您将看到所有先前的值(从其默认值开始)null)或所有后续值。您永远不会获得一些位对应于值 1 和一些位对应于值 2 的引用,这实际上会使其成为 JVM 堆中随机位置的引用 - 这将是可怕的!基本上他们是说,“引用本身要么为空,要么指向内存中的有效位置。”但什么是in那个记忆,就是事情变得奇怪的地方。

设置一个简单的例子

我假设这个简单的持有者:

public class Holder {
    int value; // NOT final!
    public Holder(int value) { this.value = value; }
}

鉴于此,当你这样做时会发生什么holder = new Holder(42)?

  1. JVM 为新的 Holder 对象分配一些空间,并为其所有字段分配默认值(即value = 0)
  2. the JVM invokes the Holder constructor
    • JVM 设置<new instance>.value到传入值 (42)。
    • 构造函数完成
  3. JVM 返回对我们刚刚分配的对象的引用,并设置Holder.holder到这个新的参考

重新排序使事情变得困难(但它也使程序变得更快!)

问题是另一个线程可以按任意顺序查看这些事件,因为它们之间没有同步点。这是因为构造函数没有任何特殊的同步或发生之前语义(这是一个小谎言,稍后会详细介绍)。您可以在以下位置查看“同步”操作的完整列表:JLS 17.4.4 http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.4;请注意,其中没有关于构造函数的内容。

因此,另一个线程可能会看到这些操作的顺序为 (1, 3, 2)。这意味着如果某个其他事件在事件 1 和 3 之间排序——例如,如果有人读Holder.holder.value到本地变量中——然后他们会看到新分配的对象,但在构造函数运行之前显示它的值:你会看到Holder.holder.value == 0。这称为部分构造的对象,它可能非常令人困惑。

如果构造函数有多个步骤(设置多个字段,或设置然后更改字段),那么您可以看到这些步骤的任何顺序。几乎所有的赌注都落空了。哎呀!

构造函数和final fields

我上面提到,当我断言构造函数没有任何特殊的同步语义时,我撒了谎。假设你不泄露this,有一个例外:任何final fields are保证被视为位于构造函数末尾(请参阅JLS 17.5 http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5).

您可以将其视为步骤 2 和步骤 3 之间存在某种同步点,但它only适用于final fields.

  • 它不适用于非最终字段
  • 它不会传递到其他同步点。
  • It does但是,可以扩展到您通过以下方式访问的任何状态final字段。所以,如果你有一个final List<String>,并且您的构造函数初始化它,然后添加一些值,然后保证所有线程都能看到该列表,至少具有它在构造函数末尾的状态,包括那些add来电。 (如果您在构造函数之后修改列表而不进行同步,那么所有的赌注都会再次取消。)

这就是为什么它在我上面的例子中很重要value不是最终的。如果是的话,你就看不到了Holder.holder.value == 0.

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

并发访问公共字段。为什么可以观察到不一致的状态? 的相关文章

随机推荐

  • 在 DataGrip 中定义 Redshift 连接

    我尝试在 DataGrip 中定义 Redshift 连接 但在 UI 中找不到任何 Redshift 驱动程序 我尝试使用 Postgres 和通用数据库驱动程序 但没有成功 有人能够配置这个吗 如果您想将 DataGrip 连接到 Re
  • GMAIL 中具有非 HTTP 架构的超链接

    因此 我正在制作移动应用程序 并希望使用相同的 URL 制作链接来激活 iOS 和 Android 中的内容 我已经知道该怎么做了 我对 Android 的意图是这样的
  • 如何在子菜单上使用滚动条时显示子菜单?

    当我有滚动条时 我的子菜单不会显示 我正在使用引导程序 这是我的html代码 a href Location a ul class dropdown menu scrollable menu li class dropdown submen
  • 如何调试eclipse插件的启动?

    我尝试将 Eclipse 插件从 Java8 迁移到 Java9 如果我启动调试会话 作为 Eclipse 应用程序运行 一切正常 但是 安装插件后我无法使用它 如果我在 OSGI 控制台中使用 ss 我的插件会得到以下状态 1102 ST
  • 使用正则表达式生成字符串而不是匹配它们[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在编写一个 Java 实用程序 可以帮助我生成大量数据以进行性能测试 这将是really能够为字符串
  • 发现存储特定面向对象数据结构的最佳方法

    经过一些奇妙的建议 以及由于最终有可能解决我的问题而兴奋的不眠之夜 我意识到我仍然没有完全找到解决方案 因此 我在这里更详细地概述我的问题 希望有人知道实现这一目标的最佳方法 回顾一下 如果你还没有读过上一篇文章 https stackov
  • 快速 count() 两个字符串数组的交集

    我需要计算两个大字符串数组的交集对应的元素数量 并且速度非常快 我正在使用以下代码 arr1 i Intersect arr2 j Count 对于 CPU 时间 VS Profiler 指示 85 1 在System Linq Enume
  • Redis扫描跳过键

    我正在使用 predis 如果有什么区别的话 可以使用 laravel php 客户端来与 Redis 一起工作 我需要从 Redis 获取与特定前缀匹配的所有键 我这样做 keys foreach new Iterator Keyspac
  • 我可以关闭 Kramdown 中的代码块吗?

    我可以关闭 Jekyll 中的代码块吗 它将使我的 md 文件更具可读性 我在内联 HTML 和包含文件中都遇到这个问题 我使用 Jekyll 创建网站 从不需要代码块 现在我将样式放入 HTML 中 如下所示 div class cool
  • Angular 5 + Angular Material Select + Reactive Forms == 没有显示初始选项

    正如标题所说 我有一个反应形式 有多个
  • 这个命令在 bash 中起什么作用: ,_,( ){ ,_,| ,_,&};,_,

    我不确定这意味着什么 看起来像 bash 命令 但它可能是 bash shell 指令或其他东西 如果有人可以帮助理解这一点 我将不胜感激 当我运行它时它杀死了我的bash It s a 叉子炸弹 http en wikipedia org
  • C++ 和 C 可变参数如何一起使用?

    通常 将 C 11 可变参数模板功能与函数一起使用需要基于可变参数的函数参数位于函数参数列表中的最后一个 有一个例外 如果存在 C 级可变参数 它们是倒数第二个参数 这必须是最后一个 template lt typename Args gt
  • 编辑 plist 中的现有数据

    通过编码检查数据是否已经存在后 如何覆盖 plist 中的现有数据 详细地 我将用户名和密码保存在位于文档目录中的 plist 文件中 现在 如果用户选择更改密码选项 plist 应检查他的用户名是否存在 如果存在 那么他输入的新密码应该覆
  • 更改微调器下拉宽度

    我需要调整该部分的大小以完全显示 我怎样才能做到这一点 我的适配器 String navigations getResources getStringArray R array actionBar ArrayAdapter
  • SSL 和中间人攻击

    我的印象是 自签名证书会带来中间人攻击的风险 因为中间人可以向客户端提供不同的自签名证书 我的问题如下 如果我在服务器上使用自签名证书 使用 SelfSSL 并且在运行 ssl 页面 这是一个管理页面 不面向公众 时 浏览器会向我发出警告
  • 如何打乱 ArrayList 的特定范围?

    在Java中 我知道要打乱ArrayList 存在Collections shuffle 方法 但是这会打乱整个列表 我如何编写一个方法 或者 有人可以编写它并向我展示吗 如下所示 private ArrayList
  • 启动 Core Data 应用程序时在后台配置 NSFetchedResultsController

    我已经使用通常的样板代码设置了一个 Core Data 应用程序 并且 RootViewController 通过调用以下命令来初始化 FRC NSFetchedResultsController fetchedResultsControl
  • websocket客户端连接服务器时出现SockJsException

    我使用带有 websocket 支持的 spring 4 0 3 我的 websocket spring 配置是
  • 如何将光标放在 NetBeans Java 文件模板中?

    我希望在 NetBeans 中创建接口 类等时将光标置于 JavaDoc 区域 您可以使用 cursor 对于非基于文件的模板 但这不适用于文件模板 lt if package package gt package package curs
  • 并发访问公共字段。为什么可以观察到不一致的状态?

    我正在阅读 B Goetz Java 并发实践 现在我正在阅读section 3 5关于安全出版 他说 Unsafe publication public Holder holder public void initialize holde