1

Consider we have classes like this:

class A {
    public B b;

    public void someFunc() { // called sometime
        b = new B();
    }
}

Class B's constructor assigns some inner variables.

Field b is not thread-safe in the sense another thread can view b not-null when B constructor hasn't finished. (during someFunc execution)

My question is the following: how can it be (from the logic perspective) that the constructor hasn't finished yet?

For me reordering of such kind is magic.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
voipp
  • 1,243
  • 3
  • 18
  • 31
  • This relates to java object initialization order, https://stackoverflow.com/questions/23093470/java-order-of-initialization-and-instantiation – Jacob Jul 12 '18 at 00:19
  • Are you asking how another thread could call `someFunc` before the constructor completes? – shmosel Jul 12 '18 at 01:14
  • @shmosel I'm interested how compiled Java code could cause reordering of class construction and link **b** initialization – voipp Jul 12 '18 at 05:18

3 Answers3

5

In the context of thread safety, this usually happens because of just-in-time (JIT) compilers. A JIT compiler takes Java byte code and translates it in to machine code to make it run faster. During translation, it's able to make a lot of optimizations, such as inlining various methods and constructors.

Supposing B had a constructor like this:

class B {
    int x;
    B(int x) { this.x = x; }
}

When a constructor is inlined, it takes Java code that's something like this:

b = new B(1);

And translates it to machine code that takes steps similar to the following:

  1. Allocate space for a B object somehow.
  2. Store the pointer to that memory in to b.
  3. Store 1 in b.x.

In other words, code which is analogous to this (in terms of ordering):

b = new B();
b.x = 1;

But we don't actually call a constructor at all. We'd just allocate a B, however the JVM does it internally, and assign b.x directly. Calling the constructor would involve jump instructions, so it's a bit faster to inline it.

There's an example like that in the famous "Double-Checked Locking is Broken" Declaration.

A regular Java compiler would be allowed to inline constructors too, but regular Java compilers don't typically perform many optimizations.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
1

Instance of object can "escape" from constructor, like that:

public class EscapeDemo {
    static void escape(B b) {
        System.out.println(b.strA);
        System.out.println(b.strB); // still null in this example, even if field is final and initialized to non-null value.
    }

    public static void main(String[] args) {
        System.out.println(new B());
    }
}
class B {
   final String strA;
   final String strB;
    B() {
        strA = "some operations";
        EscapeDemo.escape(this);
        strB = "here";
    }
}

prints:

some operations
null
B@hashcode

And in similar way that reference could escape to some code that will use it from other thread.

Like Andy Guibert added in comment: this is a bad practice to write such code - as it might be source of many weird errors and hard to trace bug - like here we have something that should not be a null, but it is a null.
And if you want to do something with object instance on creation it is much better idea to create static factory method that will create instance and then do something with (like add to some collection/registry) it and then return it.

Also if you include usage of weird code nad hacks - in bytecode java object creation is separated from constructor call, so it is possible from bytecode level to create an object, pass it somewhere and call constructor in some other place.

But otherwise field is assigned after right side expression is executed so for code

b = new B(); 

field b can be only null or B instance after constructor is called. Unless you would set that field from inside of B constructor like in my escape example.

GotoFinal
  • 3,585
  • 2
  • 18
  • 33
  • 2
    I would also add that letting `this` escape from a constructor (as you do in `EscapeDemo.escape(this)` is an anti-pattern and should be avoided, since it allows other code to operate on the object instance which is only partially initialized and often causes unintended behavior – Andy Guibert Jul 11 '18 at 23:30
0

If I understand your question correctly. You are asking about if you create an instance of Object A which has a field b of type B and the field b is not initialized when A is created but only some other object calls someFunc(). What will happen when some other thread tries to access this field b?

If so, when you create a new object of type B the JVM will allocate some memory for this object and then will return a reference which will be held in the field b. If other thread tries to access the field b before it got the reference of the new object, it will return null otherwise will return the reference to the newly created object.

Jiajie Xu
  • 41
  • 8