By voipp


2018-07-11 22:00:13 8 Comments

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.

3 comments

@Radiodef 2018-07-12 01:09:06

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.

@voipp 2018-07-12 05:20:16

thanks for the answer. +1 for article reference

@GotoFinal 2018-07-11 23:06:11

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
[email protected]

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.

@Andy Guibert 2018-07-11 23:30:42

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

@Jiajie Xu 2018-07-11 22:58:40

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.

Related Questions

Sponsored Content

62 Answered Questions

[SOLVED] How to generate random integers within a specific range in Java?

  • 2008-12-12 18:20:57
  • user42155
  • 3595457 View
  • 3022 Score
  • 62 Answer
  • Tags:   java random integer

24 Answered Questions

36 Answered Questions

[SOLVED] How do I convert a String to an int in Java?

77 Answered Questions

[SOLVED] Is Java "pass-by-reference" or "pass-by-value"?

24 Answered Questions

[SOLVED] Java inner class and static nested class

  • 2008-09-16 08:22:35
  • Omnipotent
  • 653249 View
  • 1529 Score
  • 24 Answer
  • Tags:   java inner-classes

46 Answered Questions

[SOLVED] Does a finally block always get executed in Java?

53 Answered Questions

[SOLVED] Creating a memory leak with Java

18 Answered Questions

[SOLVED] How do I call one constructor from another in Java?

  • 2008-11-12 20:10:19
  • ashokgelal
  • 700209 View
  • 1914 Score
  • 18 Answer
  • Tags:   java constructor

31 Answered Questions

[SOLVED] How to break out of nested loops in Java?

  • 2009-05-20 09:07:43
  • boutta
  • 976780 View
  • 1591 Score
  • 31 Answer
  • Tags:   java loops

24 Answered Questions

[SOLVED] How to get an enum value from a string value in Java?

  • 2009-03-02 22:56:34
  • Malachi
  • 902784 View
  • 1676 Score
  • 24 Answer
  • Tags:   java enums

Sponsored Content