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