且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

通过匿名类java引用包围对象转义

更新时间:2023-01-17 20:46:07

在第一种形式中,事件监听器对象被注册到构造函数 中的事件源

为什么这是一个问题?一旦事件监听器被注册,事件源可以在任何时候调用它的方法。想象一下,事件源正在使用的线程开始调用事件侦听器方法。



但是,由于可见性问题,这个问题比看起来糟糕。使注册是构造函数所做的最后一个操作,仍然有可能看到部分构造的对象或处于无效状态的对象。在没有正确发生的情况下,在排序之前没有可见性保证。



声明它的最终提供了发生之前排序(因此第二种形式)。


I am reading Java concurrency in practice and the below examples are from that. And my questions are What do they mean by this reference escape?. What will be the problem? . How does the this reference escapes from doSomething(e).

public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            }
        );
    }
}

How does this solves the problem

public class SafeListener {
    private final EventListener listener;
    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        };
    }
    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}

Edit :

I have tried the below examples

public class Escape {
    public  Escape( Printer printer ){
        printer.print(new Escaper(){
            @Override
            public void parentData(){
            theCulprit1(Escape.this);
            }
            public String name = "shal";
            @Override
            public void theCulprit(){
            System.out.println( this.name );
            System.out.println( Escape.this.age );
            }
        });
        canAccess();
    }
    public void  canAccess(){
    this.age = "25";
    }
    public String age = "62";
    @SuppressWarnings("unused")
    public static void main(String args[]){
    Escape escape = new Escape(new Printer());
    }
}

class Printer{
    public void print(Escaper escaper){
    escaper.theCulprit();
    escaper.parentData();
    }
}

class Escaper{
    public void parentData(){
    }
    public void theCulprit(){
    }
    public void theCulprit1(Escape escape){
    System.out.println(escape.age);
    }
}

Due to incomplete construction of the escape object This outputs shal 62 62

Where as i changed my code like this

public class Escape {
    private final Escaper escaper;
    private Escape( ){
        escaper = new Escaper(){
            @Override
            public void parentData(){
            theCulprit1(Escape.this);
            }
            public String name = "shal";
            public void theCulprit(){
            System.out.println( name );
            System.out.println( age );
            }
        };
        canAccess();
    }
    public void  canAccess(){
    age = "25";
    }
    public String age = "62";
    public static Escape newInstance( Printer printer){
    Escape escape = new Escape();
    printer.print(escape.escaper);
    return escape;
    }
    @SuppressWarnings("unused")
    public static void main(String args[]){
    Escape.newInstance(new Printer());
    }
}

Where as here.It outputs shal 25 25

Am i right ? Also is there any re-ordering of operations, because in the first example the age got initialized to 62. Even without making the escaper field final in my second example it works !

In the first form, the event listener object gets registered to the event source within the constructor, and thus it makes itself (and by association the "this" object) available to the event source before the constructor completes. If the inner class object escapes, so does the outer object.

Why is this a problem? Once the event listener is registered, the event source may invoke its methods at any time. Imagine a thread that the event source is using starts invoking the event listener methods. This now can happen even before the constructor completes.

This problem is worse than it appears, however, due to visibility issues. Even if you make the registration the "last operation" that the constructor does, there is still the possibility of seeing partially constructed object or an object in an invalid state. There is simply no visibility guarantee in the absence of a proper happens-before ordering.

Declaring it final affords that happens-before ordering (thus the second form).