Java:引用转义
阅读以下代码是“不安全构造”的一个示例,因为它允许此引用逃逸。我不太明白“这个”是怎么逃走的。我对java世界相当陌生。谁能帮我理解这一点Java:引用转义,java,concurrent-programming,Java,Concurrent Programming,阅读以下代码是“不安全构造”的一个示例,因为它允许此引用逃逸。我不太明白“这个”是怎么逃走的。我对java世界相当陌生。谁能帮我理解这一点 public class ThisEscape { public ThisEscape(EventSource source) { source.registerListener( new EventListener() { public void onEvent(Event e
public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
}
}
我猜
doSomething
方法是在thisecape
类中声明的,在这种情况下,引用当然可以“escape”。也就是说,一些事件可以在其创建之后和执行此Escape构造函数之前触发此
EventListener
。而侦听器将依次调用thisecape
的实例方法
我会稍微修改一下你的例子。现在变量var
可以在doSomething
方法中访问,然后才能在构造函数中赋值
public class ThisEscape {
private final int var;
public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
}
);
// more initialization
// ...
var = 10;
}
// result can be 0 or 10
int doSomething(Event e) {
return var;
}
}
我也有同样的怀疑 问题是,在其他类中实例化的每个类都有一个对变量
$this
中封闭类的引用
这就是java所称的“合成的”,它不是你定义的东西,而是java自动为你做的东西
如果您想亲自查看,请在
doSomething(e)
行中设置断点,并检查EventListener
的属性。您在问题中发布的示例来自Brian Goetz等人。它位于第3.2节“发布和转义”。我不会在这里重复该部分的细节。(去书架上买一本,或者向同事借一本!)
示例代码说明的问题是,构造函数允许对正在构造的对象的引用在构造函数完成创建对象之前“转义”。这是一个问题,原因有二:
thisecape
示例很隐蔽,因为引用是通过隐式传递给匿名EventListener
类构造函数的this
引用进行转义的。但是,如果过早地明确发布参考文献,同样的问题也会出现
下面是一个示例来说明不完全初始化对象的问题:
public class Thing {
public Thing (Leaker leaker) {
leaker.leak(this);
}
}
public class NamedThing extends Thing {
private String name;
public NamedThing (Leaker leaker, String name) {
super(leaker);
}
public String getName() {
return name;
}
}
如果Leaker.leak(…)
方法对泄漏的对象调用getName()
,它将得到null
。。。因为此时对象的构造函数链尚未完成
下面的示例说明了final
属性的不安全发布问题
public class Unsafe {
public final int foo = 42;
public Unsafe(Unsafe[] leak) {
leak[0] = this; // Unsafe publication
// Make the "window of vulnerability" large
for (long l = 0; l < /* very large */ ; l++) {
...
}
}
}
public class Main {
public static void main(String[] args) {
final Unsafe[] leak = new Unsafe[1];
new Thread(new Runnable() {
public void run() {
Thread.yield(); // (or sleep for a bit)
new Unsafe(leak);
}
}).start();
while (true) {
if (leak[0] != null) {
if (leak[0].foo == 42) {
System.err.println("OK");
} else {
System.err.println("OUCH!");
}
System.exit(0);
}
}
}
}
公共类不安全{
公共最终int foo=42;
公共不安全(不安全[]泄漏){
泄漏[0]=此;//发布不安全
//使“漏洞窗口”变大
对于(长l=0;l*非常大*/;l++){
...
}
}
}
公共班机{
公共静态void main(字符串[]args){
最终不安全[]泄漏=新不安全[1];
新线程(newrunnable()){
公开募捐{
Thread.yield();/(或休眠一段时间)
新的不安全(泄漏);
}
}).start();
while(true){
如果(泄漏[0]!=null){
if(泄漏[0]。foo==42){
系统错误打印项次(“正常”);
}否则{
System.err.println(“哎哟!”);
}
系统出口(0);
}
}
}
}
此应用程序的某些运行可能会打印“哎哟!”而不是“确定”,这表示主线程已观察到由于通过泄漏数组进行不安全发布,因此不安全对象处于“不可能”状态。这种情况是否发生取决于JVM和硬件平台
现在这个例子显然是人为的,但不难想象这种事情如何在真正的多线程应用程序中发生
作为JSR133的结果,Java 5(JLS的第三版)中指定了当前的Java内存模型。在此之前,Java与内存相关的方面没有得到充分的说明。参考早期版本/版本的资料已过时,但Goetz版本1中有关内存模型的信息是最新的
记忆模型的某些技术方面显然需要修改;见和。然而,这项工作还没有出现在JLS版本中。我在阅读Brian Goetz的“”时遇到了完全相同的问题
斯蒂芬C的答案(公认的答案)非常好!我只想在我发现的另一个资源的基础上再加上一个。它来自JavaExperties,Heinz M.Kabutz博士在那里准确地分析了devnull发布的代码示例。他解释了编译后生成的类(外部类、内部类)以及如何转义。我发现这个解释很有用,所以我想和大家分享:)
(他扩展了示例并提供了竞争条件。)
(他解释了编译后生成什么样的类,以及这个如何逃逸。)这不是最令人高兴的例子,因为这个问题的来源(实践中的并发)一书说,即使registerListener是构造函数中的最后一行,构造不良的对象仍然可以escape@Pablo怎么