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