在此类的对象上调用 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 的一个例子的主要内容,如果未能解决你的问题,请参考以下文章
在调用 deleteLater() 后直接删除对 Qt 对象的 Python 引用是不是安全?
使用 PHI 信息进行 SQL 调用时,WordPress $wpdb 对象是不是安全?