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