31

Goetz's Java Concurrency in Practice, page 41, mentions how this reference can escape during construction. A "don't do this" example:

public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(
            new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            });
    }
}

Here this is "escaping" via the fact that doSomething(e) refers to the enclosing ThisEscape instance. The situation can be fixed by using static factory methods (first construct the plain object, then register the listener) instead of public constructors (doing all the work). The book goes on:

Publishing an object from within its constructor can publish an incompletely constructed object. This is true even if the publication is the last statement in the constructor. If the this reference escapes during construction, the object is considered not properly constructed.

I don't quite get this. If the publication is the last statement in the constructor, hasn't all the constructing work been done before that? How come is this not valid by then? Apparently there's some voodoo going on after that, but what?

Joonas Pulakka
  • 36,252
  • 29
  • 106
  • 169
  • Instead I don't get how the this refernce would be passed to the EventListener as ```EventListener``` would be constructed first and then ```ThisEscape``` – Anand Kadhi Jun 19 '21 at 07:22

3 Answers3

20

The end of a constructor is a special place in terms of concurrency, with respect to final fields. From section 17.5 of the Java Language Specification:

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

The usage model for final fields is a simple one. Set the final fields for an object in that object's constructor. Do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.

In other words, your listener could end up seeing final fields with their default values if it examines the object in another thread. This wouldn't happen if listener registration happened after the constructor has completed.

In terms of what's going on, I suspect there's an implicit memory barrier at the very end of a constructor, making sure that all threads "see" the new data; without that memory barrier having been applied, there could be problems.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Wow, it's surprising that `final` fields, which usually are considered concurrency friendly, are the the culprit in this case! – Joonas Pulakka Mar 25 '10 at 07:22
  • @Joonas: That's the rub - they're concurrency-friendly *if you make sure the reference doesn't escape from the constructor*. In most cases that's a pretty small price to pay. – Jon Skeet Mar 25 '10 at 07:27
  • 1
    Actually, this applies to any fields, not just final ones. – Stephen C Mar 25 '10 at 07:28
  • 1
    By the way; would it suffice to make another simple constructor that initializes all the fields, and then call `this()` in the beginning of the more complex constructor that leaks `this`? Would it be analogous to a factory method? – Joonas Pulakka Mar 25 '10 at 07:39
  • I made a separate question of the previous comment: http://stackoverflow.com/questions/2513841/how-do-jvms-implicit-memory-barriers-behave-when-chaining-constructors – Joonas Pulakka Mar 25 '10 at 08:01
  • @Stephen C: If you look at the spec link, you'll see it's only specifically called out for final fields. – Jon Skeet Mar 25 '10 at 08:52
  • " A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields." That is painfully confusing to read. What makes a thread limited to only see a reference to an object after that object has been completely initialized? Are there threads that can see objects that are not completely initialized? With the way this is phrased, it seems that this guarantee only applies to some threads, not all. – tmsimont Aug 30 '18 at 22:49
  • @tmsimont: "What makes a thread limited to only see a reference to an object after that object has been completely initialized?" The specification. It's a guarantee that VMs have to implement. "Are there threads that can see objects that are not completely initialized?" Not unless you explicitly make that reference visible from within the constructor or code called by the constructor. The paragraph is basically advising you not to do that. It's saying that it you only use a reference after the constructor has finished, you can rely on any values in final fields being visible in a useful way. – Jon Skeet Aug 31 '18 at 06:32
  • It also means (as the example 17.5-1 in version 11 of the JLS makes clear) that non-final fields are not guaranteed to be initialised from the view of a different thread (aka there is no implicit fence for non-finals, only for finals). – Paul de Vrieze Nov 07 '18 at 10:15
6

Another problem arises when you subclass ThisEscape, and the child class invokes this consructor. The implicit this reference in the EventListener would have an incompletely constructed object.

Rob Heiser
  • 2,792
  • 1
  • 21
  • 28
  • 1
    Good call. In particular this could create problems if the subclass overrides virtual methods from `ThisEscape` - those overridden methods could be called before the state they require has been set up. – Jon Skeet Mar 25 '10 at 07:19
  • 1
    @Stephen C: I think this is quite a good point, definitely not off-topic. – Joonas Pulakka Mar 25 '10 at 07:40
  • @JoonasPulakka It is a good, off-topic, point. The question is about final fields. – user207421 Aug 08 '13 at 03:31
  • @EJP The question is about *incompletely constructed objects*, even though the accepted answer (unfortunately it's possible to accept only one!) discusses the somewhat esoteric behavior of final fields. – Joonas Pulakka Aug 08 '13 at 05:51
  • Why can't java has some way to indicate that object creation is complete and it is safe to use that object. So, incomplete object being referenced in any thread will wait till object is constructed completely. – ansraju Dec 05 '17 at 17:26
2

There is a small but finite time between the registerListener ending and the constructor returning. Another thread could use come in at that time and attempt to call doSomething(). If the runtime didn't return straight to your code at that time, the object could be in a invalid state.

I'm not sure of java really but one example I can think of is where possibly the runtime relocates the instance before returning to you.

Its a small chance I grant you.

Preet Sangha
  • 64,563
  • 18
  • 145
  • 216