6

In JCIP, section 3.2.1 "Safe Constructor Practices", there is a warning against leaking this to another thread from the constructor, "even if the publication is the last statement in the constructor." That last part seems too strong to me, and it's provided with no justification. What is it that happens after construction that I must be so careful to avoid? Are there exceptions? I'm interested because I recently submitted some code in which I did this very thing, and I want to decide whether there is justification to go back through and refactor.

philo
  • 3,580
  • 3
  • 29
  • 40

3 Answers3

4

You should never leak this from a constructor at any point, "even [...] in the last statement." Since this isn't fully constructed some very odd things can happen. See this SO answer on a very similar question.

Community
  • 1
  • 1
Andrew White
  • 52,720
  • 19
  • 113
  • 137
4

You should never pass this out of the constructor (known as "leaking this")

One reason you shouldn't do this, even if it's the last line of the constructor, is that the JVM is allowed to re-order statements as long as the effect on the current thread isn't affected. If this is passed to a process running in another thread, reordering can cause weird and subtle bugs.

Another reason is that subclasses may provide their own initialization, so construction may not be complete at the last line of your class' constructor.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
4

As far as Java memory model is concerned, the constructor exit plays a role in final field semantics, therefore there's a difference whether a statement is before or after constructor exit.

This works                         This doesn't work
-------------------------------------------------------------

static Foo shared;                 static Foo shared;

class Foo                          class Foo
{                                  {   
    final int i;                       final int i; 
    Foo()                              Foo() 
    {                                  {
        i = 1;                             i = 1;
                                           shared = this;  
    }                                  }
}                                  }

shared = new Foo();                new Foo();

(Note: shared is not volatile; the publication is through data race.)

The only difference between the 2 examples is assigning shared before or after constructor exit. In the second example, i=1 is allowed to be reordered after the assignment.

However, if the publication is a synchronized action, e.g. through a volatile variable, then it's ok; other threads will observe a fully initialized object; the fields don't even have to final.

Publication through data race (or doing anything through data race) is a very tricky business that requires very careful reasoning. If you avoid data race, things are much simpler. If your code contains no data race, there's no difference between leaking this immediately before constructor exit, and publishing it immediately after constructor exit.

irreputable
  • 44,725
  • 9
  • 65
  • 93