Reputation: 18806
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
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
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
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
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
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
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
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
Reputation: 1502076
Really simple example:
public class Test
{
private static Test lastCreatedInstance;
public Test()
{
lastCreatedInstance = this;
}
}
Upvotes: 22