Re: JavaMemoryModel: Issue with final fields

From: Doug Lea (dl@cs.oswego.edu)
Date: Fri Feb 08 2002 - 07:49:14 EST


Bill wrote:

> The problem is that there is far too much existing code that publish
> references to objects during the construction of that object. Any
> class that starts a thread within the constructor will typically
> expose the incompletely constructed object. Any class that creates
> instances of inner classes in their constructor will store references
> to the outer object into the heap before the outer object is
> completely constructed (whether or not this is publishing the object
> is a tricky question).
>
> Given the design of Java, there is often no way to get around this
> (other than to mandate the use of factories). Sometimes, objects do
> need to have associated threads. Doug, who knows better, had some
> examples of such objects in his JSR-166 slides.

Here's the one I think Bill had in mind:

  class LoggedService { // ...
    final Queue msgQ = new LinkedQueue();

    public void serve() throws InterruptedException {
      String status = doService();
      msgQ.put(status);
    }

    public LoggedService() { // start background thread
      Runnable logger = new Runnable() {
        public void run() {
          try {
            for(;;)
              System.out.println(msqQ.take());
          }
          catch(InterruptedException ie) {} }};
      new Thread(logger).start();
    }
  }

In practice, you rarely want to start a thread in a constructor, but I
illustrated in this way to make it fit on one slide. In any case,
aren't examples like this handled by requiring that parent thread make
available all variables to a new child thread? I'm newly confused
about where the problem is here.

The main contexts that demand the use of factory methods are ones
where you'd otherwise have something like:

class X {
  final static Vector instances = new Vector();
  X() {
    instances.add(this);
  }
}

class Y extends X {
  final Field aField;
  Y() {
    // implicit super() call here.
    aField = ...
  }
}

The subclass has no way of controlling exposure. So you'd need to
rework this to use public static factory methods with private
constructors etc, which can itself be error-prone. Since you can't
inherit static methods, each subclass needs to invoke the proper
initialization methods:

class X {
  final static Vector instances = new Vector();
  protected void init() { instances.add(this); }
  private X() {}

  public static X newX() {
    X x = new X();
    x.init();
    return x;
  }
}

class Y extends X {
  final Field aField;
  private Y() { aField = ... }

  public static Y newY() {
    Y y = new Y();
    y.init();
    return y;
  }
}

And there are many cases where you can't take this approach anyway. A
year or so ago, we discussed similar problems with the widespread use
of no-arg constructors plus init() methods in JavaBeans etc. Because
of JavaBeans instantiation conventions (normally using
Class.newInstance()), these classes rarely use factory methods that
would hide the object between default-construction and proper
initialization. Plus, they cannot usually use final fields because
the values only become known in init(). I still don't see any way for
people to deal with this except for synchronizing (at least parts of)
nearly all methods. This is not usually a big performance concern with
JavaBeans (they have other overhead anyway) but still a correctness
concern -- people often don't synchronize them because it is not
obvious that they need to.

-Doug
-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:38 EDT