Lawrence Dol Lawrence Dol - 5 months ago 22
Java Question

How do I create a thread-safe write-once read-many value in Java?

This is a problem I encounter frequently in working with more complex systems and which I have never figured out a good way to solve. It usually involves variations on the theme of a shared object whose construction and initialization are necessarily two distinct steps. This is generally because of architectural requirements, similar to applets, so answers that suggest I consolidate construction and initialization are not useful. The systems have to target Java 4 at the latest, so answers that suggest support available only in later JVMs are not useful either.

By way of example, let's say I have a class that is structured to fit into an application framework like so:

public class MyClass
{

private /*ideally-final*/ SomeObject someObject;

MyClass() {
someObject=null;
}

public void startup() {
someObject=new SomeObject(...arguments from environment which are not available until startup is called...);
}

public void shutdown() {
someObject=null; // this is not necessary, I am just expressing the intended scope of someObject explicitly
}
}


I can't make someObject final since it can't be set until startup() is invoked. But I would really like it to reflect its write-once semantics and be able to directly access it from multiple threads, preferably avoiding synchronization.

The idea being to express and enforce a degree of finalness, I conjecture that I could create a generic container, like so (UPDATE - corrected threading sematics of this class):

public class WormRef<T>
{
private volatile T reference; // wrapped reference

public WormRef() {
reference=null;
}

public WormRef<T> init(T val) {
if(reference!=null) { throw new IllegalStateException("The WormRef container is already initialized"); }
reference=val;
return this;
}

public T get() {
if(reference==null) { throw new IllegalStateException("The WormRef container is not initialized"); }
return reference;
}

}


and then in
MyClass
, above, do:

private final WormRef<SomeObject> someObject;

MyClass() {
someObject=new WormRef<SomeObject>();
}

public void startup() {
someObject.init(new SomeObject(...));
}

public void sometimeLater() {
someObject.get().doSomething();
}


Which raises some questions for me:


  1. Is there a better way, or existing Java object (would have to be available in Java 4)?



Secondarily, in terms of thread safety:


  1. Is this thread-safe provided that no other thread accesses
    someObject.get()
    until after its
    set()
    has been called. The other threads will only invoke methods on MyClass between startup() and shutdown() - the framework guarantees this.

  2. Given the completely unsynchronized
    WormReference
    container, it is ever possible under either JMM to see a value of
    object
    which is neither null nor a reference to a SomeObject? In other words, does the JMM always guarantee that no thread can observe the memory of an object to be whatever values happened to be on the heap when the object was allocated. I believe the answer is "Yes" because allocation explicitly zeroes the allocated memory - but can CPU caching result in something else being observed at a given memory location?

  3. Is it sufficient to make WormRef.reference volatile to ensure proper multithreaded semantics?



Note the primary thrust of this question is how to express and enforce the finalness of
someObject
without being able to actually mark it
final
; secondary is what is necessary for thread-safety. That is, don't get too hung up on the thread-safety aspect of this.

Answer

This is my final answer, Regis1 :

/**
 * Provides a simple write-one, read-many wrapper for an object reference for those situations
 * where you have an instance variable which you would like to declare as final but can't because
 * the instance initialization extends beyond construction.
 * <p>
 * An example would be <code>java.awt.Applet</code> with its constructor, <code>init()</code> and
 * <code>start()</code> methods.
 * <p>
 * Threading Design : [ ] Single Threaded  [x] Threadsafe  [ ] Immutable  [ ] Isolated
 *
 * @since           Build 2010.0311.1923
 */

public class WormRef<T>
extends Object
{

private volatile T                      reference;                              // wrapped reference

public WormRef() {
    super();

    reference=null;
    }

public WormRef<T> init(T val) {
    // Use synchronization to prevent a race-condition whereby the following interation could happen between three threads
    //
    //  Thread 1        Thread 2        Thread 3
    //  --------------- --------------- ---------------
    //  init-read null
    //                  init-read null
    //  init-write A
    //                                  get A
    //                  init-write B
    //                                  get B
    //
    // whereby Thread 3 sees A on the first get and B on subsequent gets.
    synchronized(this) {
        if(reference!=null) { throw new IllegalStateException("The WormRef container is already initialized"); }
        reference=val;
        }
    return this;
    }

public T get() {
    if(reference==null) { throw new IllegalStateException("The WormRef container is not initialized"); }
    return reference;
    }

} // END PUBLIC CLASS

(1) Confer the game show "So you want to be a millionaire", hosted by Regis Philburn.