在这个类的对象上调用start()是否安全? Java Concurrency实践中的一个例子

问题描述 投票:0回答:2

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

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

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

其背后的原因是,在调用new Runnable()和new Worker(mainBoard.getSubBoard(count,i))时,此引用在构造CellularAutomata对象期间逃脱

而且自从工人[]工人;和CyclicBarrier屏障;是CellularAutomata对象的字段,在该对象的start()方法中创建的线程可能无法以适当的状态查看这些对象。

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

如果我的想法需要纠正,请告诉我,我真的很感激。这个并发之旅让我充满了许多疑惑和疑问,如果你有任何其他的参考资料,我可以学习并发性和Java的并发基础,请告诉我。

谢谢

java multithreading concurrency thread-safety safe-publication
2个回答
1
投票

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

第一个相关部分(我强调):

当构造函数完成时,对象被认为是完全初始化的。在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值。

在构造函数完成之前,任何其他线程都看不到this引用,所以没关系。 this引用“逃避”构造函数没有任何魔力;相关的事情是没有其他线程应该看到它(在构造函数完成之前)。

JLS的下一段扩展了这一点(由我添加的重点和斜体):

final字段的用法模型很简单:在该对象的构造函数中设置对象的最终字段;并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用。如果遵循此原因,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。


2
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.