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

阅读以下代码是“不安全构造”的一个示例,因为它允许此引用逃逸。我不太明白“这个”是怎么逃走的。我对java世界相当陌生。谁能帮我理解这一点

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
    此应用程序的某些运行可能会打印“哎哟!”而不是“确定”,这表示主线程已观察到由于通过
    泄漏
    数组进行不安全发布,因此
    不安全
    对象处于“不可能”状态。这种情况是否发生取决于JVM和硬件平台

    现在这个例子显然是人为的,但不难想象这种事情如何在真正的多线程应用程序中发生


    作为JSR133的结果,Java 5(JLS的第三版)中指定了当前的Java内存模型。在此之前,Java与内存相关的方面没有得到充分的说明。参考早期版本/版本的资料已过时,但Goetz版本1中有关内存模型的信息是最新的


    记忆模型的某些技术方面显然需要修改;见和。然而,这项工作还没有出现在JLS版本中。

    我在阅读Brian Goetz的“”时遇到了完全相同的问题

    斯蒂芬C的答案(公认的答案)非常好!我只想在我发现的另一个资源的基础上再加上一个。它来自JavaExperties,Heinz M.Kabutz博士在那里准确地分析了devnull发布的代码示例。他解释了编译后生成的类(外部类、内部类)以及
    如何转义。我发现这个解释很有用,所以我想和大家分享:)

    (他扩展了示例并提供了竞争条件。)


    (他解释了编译后生成什么样的类,以及这个
    如何逃逸。)

    这不是最令人高兴的例子,因为这个问题的来源(实践中的并发)一书说,即使registerListener是构造函数中的最后一行,构造不良的对象仍然可以escape@Pablo怎么