当前位置: 首页 > 面试题库 >

在此类的对象上调用start()安全吗?实践中来自Java并发的示例

乌骏
2023-03-14
问题内容

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

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

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

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

而且由于Worker [] worker;
和CyclicBarrier势垒;是CellularAutomata对象的字段,在该对象的start()方法中创建的线程可能无法看到处于适当状态的那些对象。

我认为这类似于Holder的示例http://jcip.net/listings/StuffIntoPublic.java

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

请让我知道我的想法是否需要更正,我将不胜感激。这次并发之旅使我充满了很多疑问和问题,如果您有其他参考资料可以学习并发以及Java并发的基础,请告诉我。

谢谢


问题答案:

您可以阅读Java语言规范17.5的相关部分。最后的领域语义学

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

对象的构造函数完成后,就认为该对象已完全初始化。 保证只有在对象完全初始化之后才能看到对对象的引用的线程才能
确保看到该对象的最终字段的正确初始化值。

this引用不被构造完成之前任何其他线程看到的,所以它的罚款。this构造函数中的引用“转义” 没有什么神奇之处。相关的事情是(构造函数完成之前)
没有其他线程可以看到它

JLS的下一段对此进行了扩展(我添加了强调和斜体):

final字段的使用模型很简单:在该对象的构造函数中为该对象设置final字段;并且 不要 在对象的构造函数完成之前, 在另一个线程可以看到它
的地方编写对正在构造的html" target="_blank">对象的引用
。如果执行此操作,则当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。



 类似资料:
  • 问题内容: 在《 Java Concurrency In Practice》中,作者指出 不变对象可以通过任何机制发布 不可变对象可以在没有附加同步的情况下被任何线程安全地使用,即使不使用同步来发布它们。 这是否意味着以下成语可以发布不可变对象? 会有数据竞赛吗?(这意味着线程B可能无法在线程A添加的列表中看到不可变对象) 非常感谢你。 此外,作者说,如果Resource是不可变的,以下代码是安全

  • null 对于此问题的第二部分,将在另一个问题(单击此处)中详细讨论

  • 问题内容: 我对上面的示例有一个疑问,该示例是从 练习 清单14.12计数使用锁实现的信号量的 Java并发中 提取的。 我想知道为什么我们需要在构造函数中获取锁(如所示的lock.lock()被调用)。据我所知,构造函数是 原子的 (引用转义除外),因为没有其他线程可以获取该引用,因此,其他线程看不到一半构造的对象。因此,我们不需要构造函数的同步修饰符。此外,只要对象被安全发布,我们也不必担心

  • 我对上面的示例有一个问题,该示例是从Java并发实践清单14.12使用锁实现的计数信号量中提取的。 我想知道为什么我们需要获取构造函数中的锁(如图所示lock.lock()被调用)。据我所知,构造函数是原子的(除了引用转义),因为没有其他线程可以获得引用,因此,半构造对象对其他线程不可见。因此,我们不需要构造函数的同步修饰符。此外,我们也不需要担心内存可见性,只要对象安全发布。 那么,为什么我们需

  • Python流行的Requests库据说在其主页上是线程安全的,但没有给出进一步的细节。如果调用,那么是否可以将此对象安全地传递给多个线程,如下所示: 并在多个线程中使用相同的连接池发出请求? 如果是,这是推荐的方法,还是应该给每个线程一个自己的连接池?(假设所有单个连接池的总大小与一个大连接池的大小相加,就像上面的那个。)每种方法的利弊是什么?

  • 问题内容: Brian Goetz的Java Concurrency in Practice提供了一个有效的可伸缩缓存供并发使用的示例。这是该类的代码: 可能是个愚蠢的问题,但有人能告诉我该类的并发用法吗?喜欢在主? 干杯,Agata 问题答案: 这是一个计算阶乘的示例: 因此,如果两个线程尝试在完全相同的时间计算相同的阶乘,则由于线程安全,因此只有其中一个将实际执行计算。第二个线程将简单地获取第