Rational, evidence-based argument against this practice
Consider the following situation. You have a class that starts the scheduler thread, which puts tasks in a database encoded similarly to the following:
class DBEventManager { private Thread t; private Database db; private LinkedBlockingQueue<MyEvent> eventqueue; public DBEventManager() { this("127.0.0.1:31337"); } public DBEventManager(String hostname) { db = new OracleDatabase(hostname); t = new DBJanitor(this); eventqueue = new LinkedBlockingQueue<MyEvent>(); eventqueue.put(new MyEvent("Hello Database!")); t.start(); }
A database is a kind of database abstraction interface, MyEvents are generated by everything that should signal a database change, and DBJanitor is a subclass of Thread that knows how to apply MyEvents to a database. As we can see, this implementation uses the created OracleDatabase class as the database implementation.
This is all good and good, but now your project requirements have changed. Your new plugin should be able to use the existing code base, but should also be able to connect to the Microsoft Access database. You decide to solve this with a subclass:
class AccessDBEventManager extends DBEventManager() { public AccessDBEventManager(String filename) { super(); db = new MSAccessDatabase(filename); } }
But now, our decision to start the thread in the constructor returns to haunt us now. By launching the client crappy 700MHz single core pentium II, this code now has a race condition: every few starts, a database manager is created, creating databases and starting threads, sending "Hello Database!" event to the wrong database.
This is because the thread starts at the end of the constructor of the superclass ... but this is not the end of the construction, we are still initialized by the constructor of the subclass, which overrides some elements of the superclass, so when the thread jumps directly to the event dispatch in the database, it sometimes gets inside before the subclass constructor updates the database reference to the correct database.
There are at least 2 solutions for this:
You can make your class final, which will prevent it from subclassing. If you do this, you can ensure with confidence that your object is completely constructed before exposing it to any other objects (even if it has not left the constructor), thereby ensuring that odd behavior like this will not happen.
You should also take steps to prevent reordering of assignments in your constructor: you can declare fields that the thread will access as mutable, or you can wrap them in a synchronized block of any type. Each of these two parameters imposes additional restrictions on the fact that reordering can be performed by the JIT compiler, which ensures that the fields are correctly assigned when the thread accesses them.
In this situation, you are likely to argue with your boss until he allows you to make changes to the code base, which is associated with changing DBEventManager constructors to look something like this:
private Thread t;
If you had previously considered this problem in development, you might have added an additional constructor. Then you can make your Microsoft Access using DBEventManager safely using DBEventManager(new MSAccessDatabase("somefile.db"));
You can simply not do this and abandon the generally accepted method of using a separate start method and the optional static factory method or methods that call the constructor, and then the start method, for example
public start() { t.start(); } public static DBEventManager getInstance(String hostname) { DBEventManager dbem = new DBEventManager(hostname); dbem.start(); return DBEventManager; }
I am sure that I am sane, but a second opinion would be nice.