Java:安全“泄漏”通过_happens-before_ relation在最终类的构造函数中引用? [英] Java: Safe to "leak" this-reference in constructor for final class via _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 anExecutor
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屋!