Question

We've been working on a piece of code for getting singeltons. We try to get them, if the class exists in our collection we return it otherwise we create it and store it in our collection. We use double-checked locking to make sure that we don't sync on every get operation.

However according to alot of article double checked locking is broken. However i don't know if this is the case here, since the map is already initialized and the checking is done on the contains. What do you guys think? is there anyway to make the double checked locking work here?

Map <Class <?>, Object> clazzes = getClazzes ();
T singleton = null;
if (false == clazzes.containsKey (clazz))
{
     synchronized (clazzes)
     {
         if (false == clazzes.containsKey (clazz))
         {
            try
            {
                singleton = clazz.newInstance ();
                clazzes.put (clazz, singleton);
            }
            catch (InstantiationException e)
            {
                 throw new IllegalArgumentException ( "cannot instantiate class " + clazz, e);
            }
            catch (IllegalAccessException e)
            {
                  throw new IllegalArgumentException ("cannot instantiate class " +  clazz, e);
            }
         }
         else
         {
             singleton = clazz.cast (clazzes.get (clazz));
         }
      }
 }

Thanks

Was it helpful?

Solution

Ok, so I did some research. According to this document the put operation has a happens-before relation with contains keys. Hence if we use a ConcurrentHashMap the double-checked locking should work

OTHER TIPS

Not to stir the pot unnecessarily, but there are good implementations of this pattern -- something like https://code.google.com/p/guava-libraries/wiki/CachesExplained

// Construction/setup
LoadingCache<Class<?>, Object> singletons = CacheBuilder.newBuilder()
       .build(
           new CacheLoader<Class<?>, Object>() {
             public Object load(Class<?> key) throws AnyException {
               return key.newInstance();
             }
           });
// client threads
return singletons.get(String.class);

The big advantage of this class over your implementation is that callers block per key, not per map. So two threads getting two separate singletons can both proceed. Two threads getting the same singleton will wait for it to be constructed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top