2 *******************************************************************************
3 * Copyright (C) 2010-2011, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *******************************************************************************
7 package com.ibm.icu.impl;
9 import java.lang.ref.SoftReference;
10 import java.util.concurrent.ConcurrentHashMap;
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.
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.
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
27 * @author Markus Scherer, Mark Davis
29 public abstract class SoftCache<K, V, D> extends CacheBase<K, V, D> {
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);
41 if(valueRef != null) {
42 synchronized(valueRef) {
43 value = valueRef.ref.get();
47 // The instance has been evicted, its SoftReference cleared.
48 // Create and set a new instance.
49 value = createInstance(key, data);
51 valueRef.ref = new SoftReference<V>(value);
56 } else /* valueRef == null */ {
57 // We had never cached an instance for this key.
58 value = createInstance(key, data);
62 valueRef = map.putIfAbsent(key, new SettableSoftReference<V>(value));
63 if(valueRef == null) {
64 // Normal "put": Our new value is now cached.
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);
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.
79 * @param <V> Cache instance value type
81 private static final class SettableSoftReference<V> {
82 private SettableSoftReference(V value) {
83 ref = new SoftReference<V>(value);
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
92 private synchronized V setIfAbsent(V value) {
93 V oldValue = ref.get();
94 if(oldValue == null) {
95 ref = new SoftReference<V>(value);
101 private SoftReference<V> ref; // never null
103 private ConcurrentHashMap<K, SettableSoftReference<V>> map =
104 new ConcurrentHashMap<K, SettableSoftReference<V>>();