Google
 
Web unafbapune.blogspot.com

Saturday, September 01, 2007

 

Parametric Initialization On Demand Holder Idiom

Similar to the Initialization On Demand Holder (IODH) Idiom , it occurs to me that sometimes not only is there a need to access a singleton instance, which is lazily initialized if constructed for the first time, but also to initialize with parameters, which are ignored if the singleton instance has already been constructed. Also, if there is concurrent access to the singleton instance for the very first time, it doesn't matter from which thread the parameters are passed, as long as the parameters are specified by one of these threads (ie the parameters don't come "out-of-thin-air".) The API would be something like:
   SomethingMore singleton = SomethingMore.getInstance(params);
Now this begs the question: similar to the original IODH Idiom, can the implementation of such Parametric IODH Idiom be thread-safe without explicit synchronization ? See below for 2 proposed implementations. One requires the parameter to be immutable, and the other requires the parameter to be only thread-safe.
/**
 * Parametric Initialization On Demand Holder Idiom 1
 * - requires parameter to be immutable and thread-safe
 * even in the face of unsafe publication.
 * (Usually such immutability can be achieved via final fields.)
 *  
 * @author Hanson Char
 */
public class SomethingMore 
{
    /** 
     * An immutable value,
     * such as a {@link String} instance. 
     */
    private final Object value;
    private SomethingMore(Object value) { this.value = value; }
    public Object getValue() { return value; }

    /** 
     * A temporary buffer to hold the immutable value used to initialize 
     * the singleton instance of SomethingMore.
     * Note it is an unsafe publication, but that's ok as 
     * the immutable value passed in is required to be thread-safe even
     * in the face of unsafe publication.
     */
    private static Object valueHolder;
    private static class LazySomethingMoreHolder {
        public static SomethingMore something = new SomethingMore(valueHolder);
    }
    
    /**
     * Returns the singleton instance of {@link SomethingMore}.
     * 
     * @param value an immutable value used to initialize the 
     * singleton instance of SomethingMore, if the instance is 
     * constructed for the first time;  
     * ignored otherwise.  
     * Note immutability (ie lack of mutator methods) 
     * itself is not sufficient.
     * The immutable value must be thread-safe even
     * in the face of unsafe publication.
     * (Usually such immutability can be achieved via final fields.)
     * Caller of this method is responsible for passing in a
     * value which is immutable and thread-safe even in the
     * face of unsafe publication,
     * such as a {@link String} instance. 
     */
    public static SomethingMore getInstance(Object value) {
        SomethingMore.valueHolder = value;
        return LazySomethingMoreHolder.something;
    }
}
/**
 * Parametric Initialization On Demand Holder Idiom 2
 * - requires parameter to be thread-safe.
 * 
 * @author Hanson Char
 */
public class SomethingMoreSafe 
{
    /** A thread-safe value, such as a {@link String} instance. */
    private final Object value;
    private SomethingMoreSafe(Object value) { this.value = value; }
    public Object getValue() { return value; }
    
    private static final ThreadLocal<Object> tlocal = new ThreadLocal<Object>();
    private static class LazySomethingMoreSafeHolder {
        public static SomethingMoreSafe something = new SomethingMoreSafe(tlocal.get());
    }

    /**
     * Returns the singleton instance of {@link SomethingMoreSafe}.
     * 
     * @param value a thread-safe value used to initialize the 
     * singleton instance of SomethingMoreSafe, if the instance is 
     * constructed for the first time;  
     * ignored otherwise.
     * Caller of this method is responsible for passing in a
     * value which is thread-safe, 
     * such as a {@link String} instance. 
     */
    public static SomethingMoreSafe getInstance(Object value) {
        tlocal.set(value);
        try {
            return LazySomethingMoreSafeHolder.something;
        } finally {
            tlocal.remove();
        }
    }
}
Special thanks to Joe Bowbeer, David Holmes, Dhanji R. Prasanna and Jeremy Manson for their help in the JSR-166 concurrency forum. More discussion can be found here.

Comments:
exactly when would you use such a convoluted singleton?
 
Hi,
I was going through your post. I feel there is a problem with your implementation. Say if the value has some behavior associated with it, like say implementation of an interface which has multiple implementations, then the singleton guarantee will hold good, but client may be left confusing. Say a second client tries getInstance with his own value implementation, he would expect the singleton to behave as per his value's implementation but not on the value implementation of the first invocation. He might get unexpected behavior.

Also leaving the immutability / thread safe responsibility on the client is not a good option I feel as an ignorant / malicious programmer can easily break this rule.

This looks to be a problem with trying to create parametrized IODH.
 
A variation of the idiom could overcome both of your concerns. See below:

public static SomethingMore getInstance(Object value)
{
synchronized(SomethingMore.class) {
if (SomethingMore.valueHolder != null
&& !SomethingMore.valueHolder.equals(value))
throw new IllegalArgumentException("Single instance already configured with a different value " + SomethingMore.valueHolder);
SomethingMore.valueHolder = value;
}
return LazySomethingMoreHolder.something;
}

Hope this helps.
 
I would propose a better solution though it is not suitable for all cases is to use System.getProperty() and System.setProperty() to initalize the singleton,
...

public static void init(String url) {
System.setProperty(Service.MC_URL, url);
}

private Service() {
String url = System.getProperty(MC_URL);
log.debug("url= " + url);
if ( url == null) {
throw new RuntimeException("MC_URL is not initalized");
}

SERVER = url;
}
 
This is excellent, thank-you! I'm working on some nasty legacy code at the moment and need a singleton which does all the heavy work on initialisation. Luckily for me, it's single core, so the first solution works well. Only thing I added was a null check before I set the static field to the value of the parameter passed in.
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?