| /* |
| ******************************************************************************* |
| * Copyright (C) 2010-2011, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.impl; |
| |
| import java.lang.ref.SoftReference; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| /** |
| * Generic, thread-safe cache implementation, storing SoftReferences to cached instances. |
| * To use, instantiate a subclass which implements the createInstance() method, |
| * and call get() with the key and the data. The get() call will use the data |
| * only if it needs to call createInstance(), otherwise the data is ignored. |
| * |
| * By using SoftReferences to instances, the Java runtime can release instances |
| * once they are not used any more at all. If such an instance is then requested again, |
| * the get() method will call createInstance() again and also create a new SoftReference. |
| * The cache holds on to its map of keys to SoftReferenced instances forever. |
| * |
| * @param <K> Cache lookup key type |
| * @param <V> Cache instance value type |
| * @param <D> Data type for creating a new instance value |
| * |
| * @author Markus Scherer, Mark Davis |
| */ |
| public abstract class SoftCache<K, V, D> extends CacheBase<K, V, D> { |
| @Override |
| public final V getInstance(K key, D data) { |
| // We synchronize twice, once on the map and once on valueRef, |
| // because we prefer the fine-granularity locking of the ConcurrentHashMap |
| // over coarser locking on the whole cache instance. |
| // We use a SettableSoftReference (a second level of indirection) because |
| // ConcurrentHashMap.putIfAbsent() never replaces the key's value, and if it were |
| // a simple SoftReference we would not be able to reset its value after it has been cleared. |
| // (And ConcurrentHashMap.put() always replaces the value, which we don't want either.) |
| SettableSoftReference<V> valueRef = map.get(key); |
| V value; |
| if(valueRef != null) { |
| synchronized(valueRef) { |
| value = valueRef.ref.get(); |
| if(value != null) { |
| return value; |
| } else { |
| // The instance has been evicted, its SoftReference cleared. |
| // Create and set a new instance. |
| value = createInstance(key, data); |
| if (value != null) { |
| valueRef.ref = new SoftReference<V>(value); |
| } |
| return value; |
| } |
| } |
| } else /* valueRef == null */ { |
| // We had never cached an instance for this key. |
| value = createInstance(key, data); |
| if (value == null) { |
| return null; |
| } |
| valueRef = map.putIfAbsent(key, new SettableSoftReference<V>(value)); |
| if(valueRef == null) { |
| // Normal "put": Our new value is now cached. |
| return value; |
| } else { |
| // Race condition: Another thread beat us to putting a SettableSoftReference |
| // into the map. Return its value, but just in case the garbage collector |
| // was aggressive, we also offer our new instance for caching. |
| return valueRef.setIfAbsent(value); |
| } |
| } |
| } |
| /** |
| * Value type for cache items: Has a SoftReference which can be set |
| * to a new value when the SoftReference has been cleared. |
| * The SoftCache class sometimes accesses the ref field directly. |
| * |
| * @param <V> Cache instance value type |
| */ |
| private static final class SettableSoftReference<V> { |
| private SettableSoftReference(V value) { |
| ref = new SoftReference<V>(value); |
| } |
| /** |
| * If the SoftReference has been cleared, then this replaces it with a new SoftReference |
| * for the new value and returns the new value; otherwise returns the current |
| * SoftReference's value. |
| * @param value Replacement value, for when the current reference has been cleared |
| * @return The value that is held by the SoftReference, old or new |
| */ |
| private synchronized V setIfAbsent(V value) { |
| V oldValue = ref.get(); |
| if(oldValue == null) { |
| ref = new SoftReference<V>(value); |
| return value; |
| } else { |
| return oldValue; |
| } |
| } |
| private SoftReference<V> ref; // never null |
| } |
| private ConcurrentHashMap<K, SettableSoftReference<V>> map = |
| new ConcurrentHashMap<K, SettableSoftReference<V>>(); |
| } |