Search code examples
javainitializationjava.util.concurrent

How to properly lock a resource while created in Java


I have this singleton that creates objects, currently it looks like this:

public ApplicationManagerSingleton {
    ....
    private Map<String, Thing> map = new HashMap<String, Thing>();

    public Thing getThingById( String id ) {
       Thing t = null;
       if ( !map.contains(id) ) {
        t = longAndCostlyInitializationOfThing();
        map.put(id, t );
       }
       return map.get(id);
     }
 }

The obvious problem it has is, if two threads try to access the same thing, they may endup duplicating the thing.

So I used a lock:

 public ApplicationManagerSingleton {
      private Map<String, Thing> map = new HashMap<Sring, Thing>();
      public Thing getThingById(String id ) {
          synchronized( map ) {
             if (!map.contains(id)) {
                 t = initialize....
             }
             map.put(id, t);
           }
           returns map.get(id);
      }
 }

But now that's worst because I'll be locking the map for a while each time a new resource is being created in demerit of the other threads wanting different things.

I'm pretty sure it can be better with Java 5 concurrent package. Can somebody point me in the right direction?

What I want to avoid is to lock the class or the map for other threads that are interested in other things.


Solution

  • I tried these solutions and they failed at some point in my implementation, not saying they wont work in other scenarios.

    What I finally end up doing was to use a ConcurrentMap to check if the resource has been already requested or not. If not, it is created and stored somewhere else.

    ... 
    import java.util.concurrent.ConcurrentMap;
    import java.util.concurrent.ConcurrentHashMap;
    
    ... 
    private ConcurrentMap<String, Boolean> created = new ConcurrentMap<>();
    ....
    if ( created.putIfAbsent( id, Boolean.TRUE ) == null ) {
        somewhereElse.put( id, createThing() );
    }