com.ibm.icu.impl.SoftCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
/*
*******************************************************************************
* 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 Cache lookup key type
* @param Cache instance value type
* @param Data type for creating a new instance value
*
* @author Markus Scherer, Mark Davis
*/
public abstract class SoftCache extends CacheBase {
@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 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(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(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 Cache instance value type
*/
private static final class SettableSoftReference {
private SettableSoftReference(V value) {
ref = new SoftReference(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(value);
return value;
} else {
return oldValue;
}
}
private SoftReference ref; // never null
}
private ConcurrentHashMap> map =
new ConcurrentHashMap>();
}