]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/SoftCache.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / SoftCache.java
1 /*\r
2 *******************************************************************************\r
3 *   Copyright (C) 2010, International Business Machines\r
4 *   Corporation and others.  All Rights Reserved.\r
5 *******************************************************************************\r
6 */\r
7 package com.ibm.icu.impl;\r
8 \r
9 import java.lang.ref.SoftReference;\r
10 import java.util.concurrent.ConcurrentHashMap;\r
11 \r
12 /**\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
17  *\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
22  *\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
26  *\r
27  * @author Markus Scherer, Mark Davis\r
28  */\r
29 public abstract class SoftCache<K, V, D> extends CacheBase<K, V, D> {\r
30     @Override\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
40         V value;\r
41         if(valueRef != null) {\r
42             synchronized(valueRef) {\r
43                 value = valueRef.ref.get();\r
44                 if(value != null) {\r
45                     return value;\r
46                 } else {\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
50                     return value;\r
51                 }\r
52             }\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
59                 return value;\r
60             } else {\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
65             }\r
66         }\r
67     }\r
68     /**\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
72      *\r
73      * @param <V> Cache instance value type\r
74      */\r
75     private static final class SettableSoftReference<V> {\r
76         private SettableSoftReference(V value) {\r
77             ref = new SoftReference<V>(value);\r
78         }\r
79         /**\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
85          */\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
90                 return value;\r
91             } else {\r
92                 return oldValue;\r
93             }\r
94         }\r
95         private SoftReference<V> ref;  // never null\r
96     }\r
97     private ConcurrentHashMap<K, SettableSoftReference<V>> map =\r
98         new ConcurrentHashMap<K, SettableSoftReference<V>>();\r
99 }\r