Java:安全“泄漏”通过_happens-before_ relation在最终类的构造函数中引用? [英] Java: Safe to "leak" this-reference in constructor for final class via _happens-before_ relation?

查看:147
本文介绍了Java:安全“泄漏”通过_happens-before_ relation在最终类的构造函数中引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Goetz的实践中的Java并发第3.2.1节包含以下规则:

Section 3.2.1 of Goetz's "Java Concurrency in Practice" contains the following rule:


不允许这个在施工期间逃生的参考

Do not allow the this reference to escape during construction

我理解,一般来说,允许要转义此会导致其他线程看到对象的未完整构造版本,并违反 final 字段的初始化安全保证(如所讨论的那样) 此处

I understand that, in general, allowing this to escape can lead to other threads seeing incompletely constructed versions of your object and violate the initialization safety guarantee of final fields (as discussed e.g. here)

但是有没有可能安全地泄漏这个?特别是,如果你在泄漏之前建立一个发生在之前的关系?

But is it ever possible to safely leak this? In particular, if you establish a happen-before relationship prior to the leakage?

例如,官方执行官Javadoc


在将 Runnable 对象提交到<$ c之前,线程中的操作$ c>执行者 在执行之前发生,可能在另一个线程中

Actions in a thread prior to submitting a Runnable object to an Executor happen-before its execution begins, perhaps in another thread

我对Java内存模型的天真阅读理解是,类似下面的内容应该是安全的,即使它在构造函数结束之前泄漏了这个

My naive reading understanding of the Java memory model this is that something like the following should be safe, even though it's leaking this prior to the end of the constructor:

public final class Foo {
  private final String str1;
  private String str2;
  public Foo(Executor ex) {
    str1 = "I'm final";
    str2 = "I'm not";
    ex.execute(new Runnable() {
      // Oops: Leakage!
      public void run() { System.out.println(str1 + str2);}
    });
  }
}

即使我们已泄露到潜在的恶意执行者,分配给 str1 str2 发生在泄漏之前,所以对象是(出于所有意图和目的)完全构造的,即使它没有被完全初始化 JLS 17.5。

That is, even though we have leaked this to a potentially malicious Executor, the assignments to str1 and str2 happen-before the leakage, so the object is (for all intents and purposes) completely constructed, even though it has not been "completely initialized" per JLS 17.5.

请注意,我还要求该类为 final ,因为任何子类的字段都将被初始化泄漏后。

Note that I also am requiring that the class be final, as any subclass's fields would be initialized after the leakage.

我在这里遗漏了什么吗?这实际上保证是否表现良好?在我看来,它是同步捎带的合法例子(16.1.4)总的来说,我非常感谢任何涉及这些问题的额外资源的指针。

Am I missing something here? Is this actually guaranteed to be well-behaved? It looks to me like an legitimate example of "Piggybacking on synchronization" (16.1.4) In general, I would greatly appreciate any pointers to additional resources where these issues are covered.

编辑:我知道,正如@jtahlborn所说,我可以通过使用公共静态工厂来避免这个问题。我正在寻找问题的答案,以巩固我对Java内存模型的理解。

EDIT: I am aware that, as @jtahlborn noted, I can avoid the issue by using a public static factory. I'm looking for an answer of the question directly to solidify my understanding of the Java memory model.

EDIT#2 这个答案暗示了我想要达到的目标。也就是说,遵循其中引用的JLS规则足够,以保证所有最终字段的可见性。但这是否必要,或者我们是否可以利用其他发生在之前的机制来确保我们自己的可见性保证?

EDIT #2: This answer alludes to what I'm trying to get at. That is, following the rule from the JLS cited therein is sufficient for guaranteeing visibility of all final fields. But is it necessary, or can we make use of other happen-before mechanisms to ensure our own visibility guarantees?

推荐答案

你是对的。在 general 中,Java内存模型不以任何特殊方式处理构造函数。在构造函数退出之前或之后发布对象引用几乎没有什么区别。

You are correct. In general, Java memory model does not treat constructors in any special way. Publishing an object reference before or after a constructor exit makes very little difference.

当然,唯一的例外是关于 final 字段。编写最终字段的构造函数的退出定义了字段上的冻结操作;如果这个是在冻结之后发布的,即使没有发生之前的边缘,其他线程也会读取正确初始化的字段;但不是如果是在冻结之前发布的。

The only exception is, of course, regarding final fields. The exit of a constructor where a final field is written to defines a "freeze" action on the field; if this is published after the freeze, even without happens-before edges, other threads will read the field properly initialized; but not if this is published before the freeze.

有趣的是,如果存在构造函数链接,则在最小范围内定义 freeze ;例如。

Interestingly, if there is constructor chaining, freeze is defined on the smallest scope; e.g.

-- class Bar

final int x;

Bar(int x, int ignore)
{
    this.x = x;  // assign to final
}  // [f] freeze action on this.x

public Bar(int x)
{ 
    this(x, 0);
    // [f] is reached!
    leak(this); 
}

此处泄漏(此)是安全的 this.x

查看我的其他回答了解最终字段的详细信息。

See my other answer for more details on final fields.

如果 final 看起来太复杂了,那就是。我的建议是 - 忘了!不要依赖 final 字段语义来发布不安全的内容。如果程序正确同步,则无需担心 final 字段或其精细语义。不幸的是,目前的气候是尽可能地推动最终字段,给程序员带来不必要的压力。

If final seems too complicated, it is. My advice is -- forget it! Do not ever rely on final field semantics to publish unsafely. If you program is properly synchronized, you don't need to worry about final fields or their delicate semantics. Unfortunately, the current climate is to push final fields as much as possible, creating an undue pressure on programmers.

这篇关于Java:安全“泄漏”通过_happens-before_ relation在最终类的构造函数中引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆