non sequitor
non sequitor

Reputation: 18806

How does "this" escape the constructor in Java?

I've heard about this happening in non thread-safe code due to improperly constructed objects but I really don't have the concept down, even after reading about in in Goetz's book. I'd like to solidify my understanding of this code smell as I maybe doing it and not even realize it. Please provide code in your explanation to make it stick, thanks.

Upvotes: 26

Views: 7191

Answers (8)

sina zarei
sina zarei

Reputation: 117

please Look at below class:

public class Foo {

public Foo() {
}

class Bar {
    public void innserClassDoSomthing(){
        doSomthing();
    }
  }
   public void doSomthing(){}
}

as you can see the Foo class has an inner class called Bar. after compiling this class, the ByteCode class created by jvm will be as a below:

class Foo$Bar {
  Foo$Bar(Foo var1) {
      this.this$0 = var1;
  }

  public void innserClassDoSomthing() {
      this.this$0.doSomthing();
  }
}

as you can see jvm created a new class names Foo$Bar that handle the behavior between Foo and its inner class Bar. while the inner class method innserClassDoSomthing called doSomthing() in construction progress, and this.this$0 has been referencing to its outer class before the end of the outer class constructor, so an Escape has had happened here.

Upvotes: 0

Eugene Maysyuk
Eugene Maysyuk

Reputation: 3388

Here's an example of how uninitialized this of OuterClass can be accessed from inside of inner class:

public class OuterClass {

    public Integer num;

    public OuterClass() {
        Runnable runnable = new Runnable() { // might lead to this reference escape
            @Override
            public void run() {
                // example of how uninitialized this of outer class
                // can be accessed from inside of inner class
                System.out.println(OuterClass.this.num); // will print null
            }
        };
        new Thread(runnable).start();
        new Thread().start(); // just some logic to keep JVM busy
        new Thread().start(); // just some logic to keep JVM busy
        this.num = 8;
        System.out.println(this.num); // will print 8
    }

    public static void main(String[] args) {
        new OuterClass();
    }
}

Output:

null
8

Pay attention to OuterClass.this.num instruction in the code

Upvotes: 0

msangel
msangel

Reputation: 10357

public class Test extends SomeUnknownClass{
    public Test(){
        this.addListner(new SomeEventListner(){
            @Override
            void act(){}
        });
    }
}

After this operation instanse of SomeEventListner will have a link to Test object, as a usual inner class.

More examples can be find here: http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html

Upvotes: 0

John Vint
John Vint

Reputation: 40256

Steve Gilham is correct in his assesment of why double checked locking is broken. If thread A enters that method and obj is null, that thread will begin to create an instance of the object and assign it obj. Thread B can possibly enter while thread A is still instantiating that object (but not completing) and will then view the object as not null but that object's field may not have been initialized. A partially constructed object.

However, the same type of problem can arrise if you allow the keyword this to escape the constructor. Say your constructor creates an instance of an object which forks a thread, and that object accepts your type of object. Now your object may have not be fully initialized, that is some of your fields may be null. A reference to your object by the one you have created in your constructor can now reference you as a non null object but get null field values.

A bit more explanation:

Your constructor can initialize every field in your class, but if you allow 'this' to escape before any of the other objects are created, they can be null (or default primative) when viewed by other threads if 1. They are not declared final or 2. They are not declared volatile

Upvotes: 2

KLE
KLE

Reputation: 24169

Example : in a constructor, you create an event listener inner class (it has an implicit reference to the current object), and register it to a list of listener.
=> So your object can be used by another thread, even though it did not finish executing its constructor.

     public class A {

      private boolean isIt;
      private String yesItIs;

      public A() {
        EventListener el = new EventListener() { ....};
        StaticListeners.register(el);
        isIt = true;
        yesItIs = "yesItIs";
      }
     }

An additional problem that could happen later : the object A could be fully created, made available to all threads, use by another thread ... except that that thread could see the A instance as created, yesItIs with it "yesItIs" value, but not isIt! Believe it or not, this could happen ! What happen is:

=> synchronization is only half about blocking thread, the other half is about inter-thread visibility.

The reason for that Java choice is performance : inter-thread visibility would kill performance if all data would be shared with all threads, so only synchronized data is guaranteed to be shared...

Upvotes: 34

Steve Gilham
Steve Gilham

Reputation: 11277

This is the reason why double-checked locking doesn't work. The naive code

if(obj == null)
{
  synchronized(something)
  {
     if (obj == null) obj = BuildObject(...);
  }
} 
// do something with obj

is not safe because the assignment to the local variable can occur before the rest of the construction (constructor or factory method). Thus thread 1 can be in the BuildObject step, when thread 2 enters the same block, detects a non-null obj, and then proceeds to operate on an incomplete object (thread 1 having been scheduled out in mid-call).

Upvotes: 8

Michael Borgwardt
Michael Borgwardt

Reputation: 346377

public class MyClass{
    String name;    

    public MyClass(String s)
    {
        if(s==null)
        {
            throw new IllegalArgumentException();
        }
        OtherClass.method(this);
        name= s;
    }

    public getName(){ return name; }
}

In the above code, OtherClass.method() is passed an instance of MyClass which is at that point incompletely constructed, i.e. not yet fulfilling the contract that the name property is non-null.

Upvotes: 5

Jon Skeet
Jon Skeet

Reputation: 1502076

Really simple example:

public class Test
{
    private static Test lastCreatedInstance;

    public Test()
    {
        lastCreatedInstance = this;
    }
}

Upvotes: 22

Related Questions