Java 内存模型:创建最终实例字段的循环引用图(所有字段均在同一线程内分配)是否安全?

2024-01-17

比我更了解 Java 内存模型的人可以确认我对以下代码正确同步的理解吗?

class Foo {
    private final Bar bar;

    Foo() {
        this.bar = new Bar(this);
    }
}

class Bar {
    private final Foo foo;

    Bar(Foo foo) {
        this.foo = foo;
    }
}

我知道这段代码是正确的,但我还没有完成整个过程发生在之前数学。我确实找到了两个非正式的引文,表明这是合法的,尽管我对完全依赖它们有点谨慎:

Final 字段的使用模型很简单:在对象的构造函数中设置该对象的 Final 字段;并且不要将对正在构造的对象的引用写入到另一个线程可以在对象的构造函数完成之前看到它的地方。如果遵循这一点,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。它还将看到这些最终字段引用的任何对象或数组的版本,这些版本至少与最终字段一样最新。 [Java® 语言规范:Java SE 7 版本,第 17.5 节 https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5]

另一个参考:

正确构造一个对象意味着什么?它只是意味着在构造过程中不允许“逃逸”对正在构造的对象的引用。 (有关示例,请参阅安全构造技术。)换句话说,不要将对正在构造的对象的引用放置在其他线程可能能够看到它的任何地方;不要将其分配给静态字段,不要将其注册为任何其他对象的侦听器,等等。这些任务应该在构造函数完成之后完成,而不是在构造函数中完成。 [JSR 133(Java 内存模型)常见问题解答, “在新的 JMM 下,final 字段如何工作?” http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalRight]


是的,它很安全。您的代码不会引入数据竞争。因此,它是正确同步的。这两个类的所有对象在其完全初始化状态下始终对访问这些对象的任何线程可见。

对于你的例子来说,这相当形式上直接推导 http://www.slideshare.net/VladimirSitnikv/final-field-semantics:

  1. 对于正在构造线程的线程,所有观察到的字段值需要与节目顺序。为了这线程内一致性,构建时Bar, 双手Foo值被正确观察并且永远不会null。 (这可能看起来微不足道,但内存模型也调节“单线程”内存顺序。)

  2. 对于任何正在获取 a 的线程Foo实例,其引用Bar值只能通过读取final场地。这引入了一个取消引用排序在读取地址之间Foo对象以及指向该对象的字段的取消引用Bar实例。

  3. 如果另一个线程因此能够观察到Foo完全实例(用正式术语来说,存在一个记忆链),该线程保证观察到这一点Foo完全建成,这意味着它Bar字段包含完全初始化的值。

请注意,即使Bar实例的字段是它本身final如果实例只能通过读取Foo。添加修饰符不会造成损害,并且可以更好地记录意图,因此您应该添加它。但是,就内存模型而言,即使没有它也没关系。

请注意,您引用的 JSR-133 食谱仅描述了内存模型的实现,而不是内存模型本身。在很多方面,它都过于严格。有一天,OpenJDK 可能不再与此实现保持一致,而是实现一种不太严格但仍能满足正式要求的模型。永远不要针对实现进行编码,而应始终针对规范进行编码!例如,不要依赖于构造函数之后放置的内存屏障,这就是 HotSpot 或多或少实现它的方式。这些东西不能保证保留,甚至可能因不同的硬件架构而异。

引用的规则是你永远不应该让this参考escape从构造函数角度看问题也太狭隘了。你不应该让它逃到另一个线程。例如,如果您将其交给虚拟分派方法,您将无法再控制实例的最终位置。因此,这是一个非常糟糕的做法!但是,构造函数不会被虚拟地调度,您可以按照您所描述的方式安全地创建循环引用。 (我假设你可以控制Bar及其未来的变化。在共享代码库中,您应该严格记录以下构造函数Bar一定不要让参考文献溜走。)

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

Java 内存模型:创建最终实例字段的循环引用图(所有字段均在同一线程内分配)是否安全? 的相关文章

  • Ruby 1.9.3 多核?

    昨天我读了一些关于 ruby 中线程的内容 比如本文 http www engineyard com blog 2011 ruby concurrency and you 而我一般的理解是 除了一些像 JRuby 这样的实现 有所谓的全局解
  • 如何制作具有两个索引的 Map?

    我在java中有一张这样的地图 Map
  • 确定列表编号是否连续

    我在 Java 工作 我有一个无序列表 包含 5 个数字 范围从 0 100 没有重复 我想检测其中 3 个数字是否连续且没有间隙 例子 9 12 13 11 10 true 17 1 2 3 5 true 19 22 23 27 55 f
  • 匿名类*总是*维护对其封闭实例的引用吗?

    我正在处理一些代码 其中一个对象 foo 正在创建另一个对象 对象 bar 并将其传递给Callable 之后 foo 将返回 bar 然后我希望 foo 变得无法访问 即 可用于 垃圾收集 我最初的想法是创建Callable匿名 例如 c
  • 多线程读取xml文件

    我进行了很多搜索 但找不到适合我的问题的解决方案 我编写了一个 xml 文件 其中包含电视节目的所有剧集信息 它大小 38 kb 包含大约 680 个变量的属性和字符串 起初 我只是在 XMLTextReader 的帮助下阅读它 它在我的四
  • Junit测试中LocalDateTime反序列化的问题

    我有问题LocalDateTime反序列化Junit测试 我有简单的REST API返回一些DTO目的 当我呼叫端点时 响应没有问题 它是正确的 然后我尝试编写单元测试 得到MvcResult并使用ObjectMapper将其转换为我的DT
  • 通过 Session.update 和 HibernateTemplate.merge 进行 Hibernate 更新的区别

    我看到了更新操作的类型 第一的 getHibernateTemplate execute new HibernateCallback public Object doInHibernate Session session session f
  • 从多个地方绘制 JPanel

    我目前正在为学校开发一款 Java 2D 游戏 我们必须使用抽象工厂设计模式 对于 2D 实现 我使用工厂如下 public class Java2DFact extends AbstractFactory public Display d
  • Java 中内存高效的稀疏数组

    关于时间高效的稀疏数组存在一些问题 但我正在寻找内存效率 我需要相当于List
  • Spring Security登录返回404

    我目前正在使用 Spring 框架开发我的博客 我正在实现 Spring Security 用于登录目的 一切都按预期进行 直到我提交始终返回 404 代码的登录凭据 这是我的 web xml 代码e
  • 有什么理由不在Python中混合使用多处理和线程模块

    我正在考虑使用Python来实现一个需要大量多线程的程序 另一个要求是它将在桌面上运行 因此拥有许多进程将使应用程序显得混乱且难以杀死 在任务管理器中 因此 我正在考虑使用线程和多处理模块来减少进程数量 据我了解 GIL 仅适用于单个进程
  • 使用枚举变量切换字符串

    我有一个具有不同值的枚举 并且想要切换字符串变量 现在 我在尝试将枚举值转换为字符串 可以用作大小写常量 时遇到了困难 我最好的尝试是将枚举转换为字符串数组 但开关似乎不接受数组值作为大小写常量 IntelliJ 说 需要恒定的表达 Enu
  • Jackson 中没有注释的多态反序列化

    我有一个CloudEvent
  • 如何在不打开浏览器的情况下查看 Android 应用程序中的网页?

    嘿 我正在开发一个 Android 应用程序 我想连接到该应用程序内的网络 不过 我在某种程度上尝试过 WebView 但它在我的目录中显示的文件很好 但当连接到 google com 时 它显示错误 然后我添加了这个文件
  • Android中计算两个时间之间的差异

    我有两个字符串变量 例如 StartTime 和 EndTime 我需要通过用 StartTime 减去 EndTime 来计算 TotalTime StartTime和EndTime的格式如下 StartTime 08 00 AM End
  • mongodb/node.js 中单文档并发读写操作的问题

    编辑 6 15我尝试运行相同的代码 在调用之前添加延迟 doSafePush 再次收到 ConcurrencyDBError 时 即执行return when resolve wait delay 35 then function doSa
  • Java 8 流过滤器 - 基于排序的更新

    我正在尝试对过滤器中的字段进行排序 输入文件 样本记录 DocumentList Document id 5975ff00a213745b5e1a8ed9 u id mailboxcontent id 5975ff00a213745b5e1
  • 在Java中将32bpp图像转换为16bpp图像

    如何使用 Java 库将 32bpp 图像 ARGB 转换为 16bpp 图像 ARGB 出于我的好奇 在像素级别 这种转换有什么作用 如果我有一个保存像素值 包含所有通道 的 int 值 那么在转换发生后该 int 会有什么不同 32 位
  • 如何在Java中添加两个“卡”的值?

    我正在开发一个项目来模拟二十一点游戏中的第一笔交易 到目前为止 程序创建了两张随机等级 ACE 到 KING 和随机花色的牌 我正在努力创建一个切换表或 if else 梯形图 将两张卡的附加值分配为可变分数 下面的代码从概念上代表了我想要
  • java 更新进度条

    我有一个 JFrame 和以下组件 JButton jButton1 Progress Bar ProgressBar 及其公共静态 JLabel 状态及其公共静态 单击按钮时会执行不同的语句 我想在每个语句后更新我的进度条 这是我的代码

随机推荐