java - java多线程中的安全构造

标签 java multithreading

这是在 java 多线程中安全构造实践的上下文中。我正在阅读 JCIP 书,有人可以在下面解释一下吗:

an object is in a predictable, consistent state only after its constructor returns, so publishing an object from within its constructor can publish an incompletely constructed object. This is true even if the publication is the last statement in the constructor.

在这里我想了解具体的部分'即使发布是这样也是如此 构造函数中的最后一条语句'

我知道从构造函数启动一个线程是不安全的,但是当它是构造函数中的最后一条语句时它也不安全吗?

我的猜测是关于为什么它不安全的原因是,JVM 可以对语句进行重新排序,而最后一个语句将不再是最后一个。请对此发表评论或纠正我的理解。

编辑:有问题的是,启动线程的意思是泄漏 this 引用。这种泄漏可能通过多种方式发生,例如发布事件或启动线程。

最佳答案

这里有两个见解可能对您有所帮助。首先,出于同步目的,构造函数(大部分)与任何其他方法一样;也就是说,它本身并不提供任何(除非如下所述)。其次,线程安全始终存在于各个操作之间。

假设您有以下构造函数:

MyClass() {
    this.i = 123;
    MyClass.someStaticInstance = this; // publish the object
}

// and then somewhere else:
int value = MyClass.someStaticInstance.i;

问题是:最后一个表达式能做什么?

  • 它可以抛出 NullPointerException,如果 someStaticInstance尚未设置。
  • 它也可能导致 value == 123
  • 但有趣的是,它导致value == 0

最后一点的原因是 Action 可以重新排序,构造函数在这方面并不特殊。让我们仔细看看所涉及的操作:

  • A.为新实例分配空间,并将其所有字段设置为其默认值(0 表示 int i )
  • B.设置 <instance>.i = 123
  • C.设置 someStaticInstance = <instance>
  • D.读书someStaticInstance ,然后是它的 i

如果你稍微重新排序,你可以得到:

  • A.为新实例分配空间,并将其所有字段设置为其默认值(0 表示 int i )
  • C.设置 someStaticInstance = <instance>
  • D.读书someStaticInstance ,然后是它的 i
  • B.设置 <instance>.i = 123

这就是你的 -- value是 0,而不是 123。

JCIP 还警告您泄漏可能以微妙的方式发生。例如,假设您没有明确设置 someStaticInstance字段,而只是调用 someListener.register(this) .您仍然泄露了引用,并且您应该假设您注册的监听器可能会对它做一些危险的事情,比如将它分配给 someStaticInstance .

即使i也是如此字段是最终的。您可以从 final 字段获得一些线程安全性,但前提是您不泄漏 this来自构造函数。具体来说,在 JLS 17.5 ,它说:

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

保证“只有在对象完全初始化后才能看到对该对象的引用”的唯一方法是不从其构造函数中泄漏引用。否则,您可以想象一个线程读取 MyClass.someStaticInstance在设置字段之后,但在 JVM 将构造函数识别为已完成之前。

关于java - java多线程中的安全构造,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48171140/

相关文章:

java - `Thread.currentThread()` 在构造和运行时不恒定?

python - 如何捕获多线程的异常?

java - Java-这种使用java/lang/ProcessBuilder。<init>([Ljava/lang/String;)V可能容易受到命令注入(inject)(Sonar)的攻击

java - 如何在DB中插入十进制值?

java - java中的ByteBuffer在没有写入任何内容时返回数据,不会抛出异常

python - 我的脚本需要同时运行两个循环来读取来自两个不同来源的 IO 数据。我的线程正确吗?

c++ - 如何在 C++ 中使用 <threads> 和一维数组进行矩阵乘法?

java - WorkManager 和高工作负载

java - 如何在特定连接上使用不同的证书?

java - (Eclipse RCP) 如何在工具栏中的菜单条目后添加快捷文本?