在此类的对象上调用 start() 是不是安全? Java Concurrency in practice 的一个例子

Posted

技术标签:

【中文标题】在此类的对象上调用 start() 是不是安全? Java Concurrency in practice 的一个例子【英文标题】:Is calling start() on a object of this class safe? An example from Java Concurrency in practice在此类的对象上调用 start() 是否安全? Java Concurrency in practice 的一个例子 【发布时间】:2018-06-07 17:56:46 【问题描述】:

首先,我将提供指向我将要讨论的源代码的链接,因为复制/粘贴会使这个问题页面变得太长。

在 JCIP 的清单 5.15 http://jcip.net/listings/CellularAutomata.java 中,我想在某个 main 方法中,将创建一个 CellularAutomata 对象,然后对该对象调用 start()。

但是,这样做可以吗?当对象的 start 方法被调用时,它将创建 N(处理器数量)线程和 Worker 实例。似乎用 worker 对象创建的 N 个线程可能会看到该 Worker 的不完整引用或对象。

其背后的原因是,this 引用在调用时在 CellularAutomata 对象的构造过程中转义 new Runnable() 和 new Worker(mainBoard.getSubBoard(count, i))

而且由于 Worker[] 工人;和 CyclicBarrier 屏障;是 CellularAutomata 对象的字段,在该对象的 start() 方法中创建的线程可能无法看到这些处于正确状态的对象。

我认为这类似于持有人的示例http://jcip.net/listings/StuffIntoPublic.java http://jcip.net/listings/Holder.java 其他线程可能看不到持有人的字段。 我知道 Holder 示例存在问题,因为该字段不是最终的,因此可能不可见,并且在 CellularAutomata 中它们是最终的。我读到只有最终字段的类在发布时保证其字段的可见性。但是,我还读到虽然 final 字段可能是类的唯一字段,但如果该类没有正确构造,那么这种保证就没有了。在这个例子中,由于 this 引用转义,我认为它没有正确构造。这是一个隐式让 this 引用转义的示例,这与 CellularAutomata 中的情况类似。 http://jcip.net/listings/ThisEscape.java

如果我的想法需要纠正,请告诉我,我将不胜感激。这个并发之旅让我充满了很多疑问和问题,如果您有任何其他关于我可以在哪里学习并发以及 Java 中的并发基础的参考资料,请告诉我。

谢谢

【问题讨论】:

你可能可以和你的同学讨论:-) ***.com/questions/47986235/… 我的天哪 xD...问题是,那个人就是我,但我不知道那个帐户的密码...所以一旦我回到家,我就无法登录我的电脑.哈哈,我早就认出了你的名字:) 【参考方案1】:

允许this 逃跑的危险在于它可能在它完全构建之前被看到。在这种情况下,这不是问题,因为在调用 start() 之前,runnable 不会执行,这必须在构造函数完成之后。

此外,除了final 字段保证之外,在mainBoard 的分配和runnable 的执行之间至少还有两个额外的happens-before 障碍。一个是调用Thread.start() 将是the last thread entering the barrier,即happens-before any action in the started thread. 然后是对CylicBarrier.await() 的实际调用,即happen[s]-before actions that are part of the barrier action。

所以我会说代码非常安全。

【讨论】:

非常感谢您!我希望我可以按向上按钮,但没有足够的声望点。您还回答了我的问题以及我对正确初始化和发布的疑问和错误想法【参考方案2】:

您可以阅读Java语言规范的相关部分:17.5. final Field Semantics

第一个相关部分(我添加的重点):

一个对象被认为是完全初始化的,当它 构造函数完成。 只能看到对 对象完全初始化后的对象是有保证的 查看该对象最终的正确初始化值 字段。

在构造函数完成之前,任何其他线程都看不到this 引用,所以没关系。 this 引用从构造函数中“转义”并没有什么神奇之处;相关的事情是没有其他线程应该看到它(在构造函数完成之前)。

JLS 中的下一段对此进行了扩展(重点和斜体由我添加):

final字段的使用模型很简单:设置final字段 对于该对象的构造函数中的对象;并且不要写 对在一个地方构造的对象的引用另一个地方 线程可以在对象的构造函数完成之前看到它。如果这 紧随其后,然后当另一个线程看到该对象时, 线程将始终看到正确构造的版本 对象的最终字段。

【讨论】:

这完全纠正了我对正确初始化和发布的错误想法。谢谢你,也喜欢你如何包含 JLS 重要部分

以上是关于在此类的对象上调用 start() 是不是安全? Java Concurrency in practice 的一个例子的主要内容,如果未能解决你的问题,请参考以下文章

在同一个线程上调用 start 方法两次是不是合法?

在调用 deleteLater() 后直接删除对 Qt 对象的 Python 引用是不是安全?

unity 中函数调用的顺序

使用 PHI 信息进行 SQL 调用时,WordPress $wpdb 对象是不是安全?

在多线程系统中使用静态 java.sql.Connection 实例是不是安全?

Python 的 Requests 库线程中的 Session 对象是不是安全?