View Javadoc
1   package org.pojomatic.internal;
2   
3   import java.util.concurrent.ConcurrentHashMap;
4   import java.util.concurrent.ConcurrentMap;
5   
6   /**
7    * A thread-safe "map" which generates values on demand, with the guarantee that no more than one
8    * value will be auto-created for a given key.
9    * Classes extending this class should override {@link #create(Object)}.
10   * @param <K> the key type
11   * @param <V> the value type
12   */
13  public abstract class SelfPopulatingMap<K, V> {
14  
15    public V get(final K key) {
16      V value = valueMap.get(key);
17      if (value == null) {
18        final Object mutex = new Object();
19        synchronized (mutex) {
20          Object existingMutex = mutexMap.putIfAbsent(key, mutex);
21          if (existingMutex == null) {
22            return tryCreatingValue(key);
23          }
24          else {
25            synchronized (existingMutex) {
26              V oldValue = valueMap.get(key);
27              // if the previous holder of this mutex failed to create a value, we'll give it a shot.
28              return oldValue != null ? oldValue : tryCreatingValue(key);
29            }
30          }
31        }
32      }
33      return value;
34    }
35  
36    /**
37     * Create a value for a key.  This will be called by {@link #get(Object)} when there is not
38     * already an existing value, and no other thread is already creating a value for that key.
39     * The value returned must not be null.
40     * @param key the key to create the value for
41     * @return the value
42     */
43    protected abstract V create(K key);
44  
45    private V tryCreatingValue(K key) {
46      V value = create(key);
47      valueMap.put(key, value);
48      return value;
49    }
50  
51    /**
52     * The values held by this map.
53     */
54    private final ConcurrentMap<K, V> valueMap = new ConcurrentHashMap<>();
55  
56    /**
57     * Mutexes created on demand to ensure that only a single value is created for each key.
58     */
59    private final ConcurrentMap<K, Object> mutexMap = new ConcurrentHashMap<>();
60  }
61