How does `this` reference to an outer class escape through publishing inner class instance?

前端 未结 3 1023
心在旅途
心在旅途 2020-11-28 06:36

This was asked slightly differently earlier but asking for a yes/no answer but I\'m looking for the explanation that\'s missing from the book (Java Concurrency in Practice),

3条回答
  •  感动是毒
    2020-11-28 07:17

    I'll modify the example a bit, to make it more clear. Consider this class:

    public class ThisEscape {
    
        Object someThing;
    
        public ThisEscape(EventSource source) {
            source.registerListener(
                new EventListener() {
                    public void onEvent(Event e) {
                        doSomething(e, someThing);
                    }
                });
            someThing = initTheThing();
        }
    }
    

    Behind the scenes, the anonymous inner class has access to the outer instance. You can tell this, because you can access the instance variable someThing and, as Shashank mentioned you can access the outer instance via ThisEscape.this.

    The problem is that by giving the anonymous inner class instance to the outside (in this case the EventSource object) it will also carry the ThisEscape instance with it.

    What can happen bad with it? Consider this implementation of EventSource below:

    public class SomeEventSource implements EventSource {
    
        EventListener listener;
    
        public void registerListener(EventListener listener) {
            this.listener = listener;
        }
    
        public void processEvent(Event e) {
            listener.onEvent(e);
        }
    
    }
    

    In ThisEscape's constructor we register an EventListener which will be stored in the listener instance variable.

    Now consider two threads. One is calling the ThisEscape constructor, while the other calls processEvent with some event. Also, let's say the JVM decides to switch from the first thread to the second one, right after the source.registerListener line and right before someThing = initTheThing(). The second thread now runs, and it will call the onEvent method, which as you can see, does something with someThing. But what is someThing? It's null, because the other thread did not finish initializing the object, so this will (probably) cause a NullPointerException, which is not actually what you want.

    To sum up: be careful that you don't escape objects that have not been fully initialized (or in other words, their constructor did not finish yet). One subtle way you could inadvertently do this, is by escaping anonymous inner classes from the constructor, which will implicitly escape the outer instance, which is not fully initialized.

提交回复
热议问题