Home | History | Annotate | Download | only in impl
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 //  2016 and later: Unicode, Inc. and others.
      3 // License & terms of use: http://www.unicode.org/copyright.html#License
      4 /*
      5 *******************************************************************************
      6 *   Copyright (C) 2010-2016, International Business Machines
      7 *   Corporation and others.  All Rights Reserved.
      8 *******************************************************************************
      9 */
     10 package android.icu.impl;
     11 
     12 import java.util.concurrent.ConcurrentHashMap;
     13 
     14 /**
     15  * Generic, thread-safe cache implementation, usually storing cached instances
     16  * in {@link java.lang.ref.Reference}s via {@link CacheValue}s.
     17  * To use, instantiate a subclass which implements the createInstance() method,
     18  * and call get() with the key and the data. The get() call will use the data
     19  * only if it needs to call createInstance(), otherwise the data is ignored.
     20  *
     21  * <p>When caching instances while the CacheValue "strength" is {@code SOFT},
     22  * the Java runtime can later release these instances once they are not used any more at all.
     23  * If such an instance is then requested again,
     24  * the getInstance() method will call createInstance() again and reset the CacheValue.
     25  * The cache holds on to its map of keys to CacheValues forever.
     26  *
     27  * <p>A value can be null if createInstance() returns null.
     28  * In this case, it must do so consistently for the same key and data.
     29  *
     30  * @param <K> Cache lookup key type
     31  * @param <V> Cache instance value type (must not be a CacheValue)
     32  * @param <D> Data type for creating a new instance value
     33  *
     34  * @author Markus Scherer, Mark Davis
     35  * @hide Only a subset of ICU is exposed in Android
     36  */
     37 public abstract class SoftCache<K, V, D> extends CacheBase<K, V, D> {
     38     private ConcurrentHashMap<K, Object> map = new ConcurrentHashMap<K, Object>();
     39 
     40     @SuppressWarnings("unchecked")
     41     @Override
     42     public final V getInstance(K key, D data) {
     43         // We synchronize twice, once in the ConcurrentHashMap and
     44         // once in valueRef.resetIfCleared(value),
     45         // because we prefer the fine-granularity locking of the ConcurrentHashMap
     46         // over coarser locking on the whole cache instance.
     47         // We use a CacheValue (a second level of indirection) because
     48         // ConcurrentHashMap.putIfAbsent() never replaces the key's value, and if it were
     49         // a simple Reference we would not be able to reset its value after it has been cleared.
     50         // (And ConcurrentHashMap.put() always replaces the value, which we don't want either.)
     51         Object mapValue = map.get(key);
     52         if(mapValue != null) {
     53             if(!(mapValue instanceof CacheValue)) {
     54                 // The value was stored directly.
     55                 return (V)mapValue;
     56             }
     57             CacheValue<V> cv = (CacheValue<V>)mapValue;
     58             if(cv.isNull()) {
     59                 return null;
     60             }
     61             V value = cv.get();
     62             if(value != null) {
     63                 return value;
     64             }
     65             // The instance has been evicted, its Reference cleared.
     66             // Create and set a new instance.
     67             value = createInstance(key, data);
     68             return cv.resetIfCleared(value);
     69         } else /* valueRef == null */ {
     70             // We had never cached an instance for this key.
     71             V value = createInstance(key, data);
     72             mapValue = (value != null && CacheValue.futureInstancesWillBeStrong()) ?
     73                     value : CacheValue.getInstance(value);
     74             mapValue = map.putIfAbsent(key, mapValue);
     75             if(mapValue == null) {
     76                 // Normal "put": Our new value is now cached.
     77                 return value;
     78             }
     79             // Race condition: Another thread beat us to putting a CacheValue
     80             // into the map. Return its value, but just in case the garbage collector
     81             // was aggressive, we also offer our new instance for caching.
     82             if(!(mapValue instanceof CacheValue)) {
     83                 // The value was stored directly.
     84                 return (V)mapValue;
     85             }
     86             CacheValue<V> cv = (CacheValue<V>)mapValue;
     87             return cv.resetIfCleared(value);
     88         }
     89     }
     90 }
     91