1 /* 2 ******************************************************************************* 3 * Copyright (C) 2010-2011, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ******************************************************************************* 6 */ 7 package com.ibm.icu.impl; 8 9 import java.lang.ref.SoftReference; 10 import java.util.concurrent.ConcurrentHashMap; 11 12 /** 13 * Generic, thread-safe cache implementation, storing SoftReferences to cached instances. 14 * To use, instantiate a subclass which implements the createInstance() method, 15 * and call get() with the key and the data. The get() call will use the data 16 * only if it needs to call createInstance(), otherwise the data is ignored. 17 * 18 * By using SoftReferences to instances, the Java runtime can release instances 19 * once they are not used any more at all. If such an instance is then requested again, 20 * the get() method will call createInstance() again and also create a new SoftReference. 21 * The cache holds on to its map of keys to SoftReferenced instances forever. 22 * 23 * @param <K> Cache lookup key type 24 * @param <V> Cache instance value type 25 * @param <D> Data type for creating a new instance value 26 * 27 * @author Markus Scherer, Mark Davis 28 */ 29 public abstract class SoftCache<K, V, D> extends CacheBase<K, V, D> { 30 @Override getInstance(K key, D data)31 public final V getInstance(K key, D data) { 32 // We synchronize twice, once on the map and once on valueRef, 33 // because we prefer the fine-granularity locking of the ConcurrentHashMap 34 // over coarser locking on the whole cache instance. 35 // We use a SettableSoftReference (a second level of indirection) because 36 // ConcurrentHashMap.putIfAbsent() never replaces the key's value, and if it were 37 // a simple SoftReference we would not be able to reset its value after it has been cleared. 38 // (And ConcurrentHashMap.put() always replaces the value, which we don't want either.) 39 SettableSoftReference<V> valueRef = map.get(key); 40 V value; 41 if(valueRef != null) { 42 synchronized(valueRef) { 43 value = valueRef.ref.get(); 44 if(value != null) { 45 return value; 46 } else { 47 // The instance has been evicted, its SoftReference cleared. 48 // Create and set a new instance. 49 value = createInstance(key, data); 50 if (value != null) { 51 valueRef.ref = new SoftReference<V>(value); 52 } 53 return value; 54 } 55 } 56 } else /* valueRef == null */ { 57 // We had never cached an instance for this key. 58 value = createInstance(key, data); 59 if (value == null) { 60 return null; 61 } 62 valueRef = map.putIfAbsent(key, new SettableSoftReference<V>(value)); 63 if(valueRef == null) { 64 // Normal "put": Our new value is now cached. 65 return value; 66 } else { 67 // Race condition: Another thread beat us to putting a SettableSoftReference 68 // into the map. Return its value, but just in case the garbage collector 69 // was aggressive, we also offer our new instance for caching. 70 return valueRef.setIfAbsent(value); 71 } 72 } 73 } 74 /** 75 * Value type for cache items: Has a SoftReference which can be set 76 * to a new value when the SoftReference has been cleared. 77 * The SoftCache class sometimes accesses the ref field directly. 78 * 79 * @param <V> Cache instance value type 80 */ 81 private static final class SettableSoftReference<V> { SettableSoftReference(V value)82 private SettableSoftReference(V value) { 83 ref = new SoftReference<V>(value); 84 } 85 /** 86 * If the SoftReference has been cleared, then this replaces it with a new SoftReference 87 * for the new value and returns the new value; otherwise returns the current 88 * SoftReference's value. 89 * @param value Replacement value, for when the current reference has been cleared 90 * @return The value that is held by the SoftReference, old or new 91 */ setIfAbsent(V value)92 private synchronized V setIfAbsent(V value) { 93 V oldValue = ref.get(); 94 if(oldValue == null) { 95 ref = new SoftReference<V>(value); 96 return value; 97 } else { 98 return oldValue; 99 } 100 } 101 private SoftReference<V> ref; // never null 102 } 103 private ConcurrentHashMap<K, SettableSoftReference<V>> map = 104 new ConcurrentHashMap<K, SettableSoftReference<V>>(); 105 } 106