一、前言
关于apace的shiro源码包org.apache.shiro.util.SoftHashMap自定义软引用Map实现类,依赖java.lang.ref.SoftReference、java.util.concurrent.ConcurrentHashMap类,以实现存储对象为软引用安全的HashMap类型容器。
二、源码说明
package org.apache.shiro.util;@b@@b@import java.lang.ref.ReferenceQueue;@b@import java.lang.ref.SoftReference;@b@import java.util.*;@b@import java.util.concurrent.ConcurrentHashMap;@b@import java.util.concurrent.ConcurrentLinkedQueue;@b@import java.util.concurrent.locks.ReentrantLock;@b@@b@ @b@public class SoftHashMap<K, V> implements Map<K, V> {@b@@b@ @b@ private static final int DEFAULT_RETENTION_SIZE = 100;@b@@b@ @b@ private final Map<K, SoftValue<V, K>> map;@b@@b@ /**@b@ * The number of strong references to hold internally, that is, the number of instances to prevent@b@ * from being garbage collected automatically (unlike other soft references).@b@ */@b@ private final int RETENTION_SIZE;@b@@b@ /**@b@ * The FIFO list of strong references (not to be garbage collected), order of last access.@b@ */@b@ private final Queue<V> strongReferences; //guarded by 'strongReferencesLock'@b@ private final ReentrantLock strongReferencesLock;@b@@b@ /**@b@ * Reference queue for cleared SoftReference objects.@b@ */@b@ private final ReferenceQueue<? super V> queue;@b@@b@ /**@b@ * Creates a new SoftHashMap with a default retention size size of@b@ * {@link #DEFAULT_RETENTION_SIZE DEFAULT_RETENTION_SIZE} (100 entries).@b@ *@b@ * @see #SoftHashMap(int)@b@ */@b@ public SoftHashMap() {@b@ this(DEFAULT_RETENTION_SIZE);@b@ }@b@@b@ /**@b@ * Creates a new SoftHashMap with the specified retention size.@b@ * <p/>@b@ * The retention size (n) is the total number of most recent entries in the map that will be strongly referenced@b@ * (ie 'retained') to prevent them from being eagerly garbage collected. That is, the point of a SoftHashMap is to@b@ * allow the garbage collector to remove as many entries from this map as it desires, but there will always be (n)@b@ * elements retained after a GC due to the strong references.@b@ * <p/>@b@ * Note that in a highly concurrent environments the exact total number of strong references may differ slightly@b@ * than the actual <code>retentionSize</code> value. This number is intended to be a best-effort retention low@b@ * water mark.@b@ *@b@ * @param retentionSize the total number of most recent entries in the map that will be strongly referenced@b@ * (retained), preventing them from being eagerly garbage collected by the JVM.@b@ */@b@ @SuppressWarnings({"unchecked"})@b@ public SoftHashMap(int retentionSize) {@b@ super();@b@ RETENTION_SIZE = Math.max(0, retentionSize);@b@ queue = new ReferenceQueue<V>();@b@ strongReferencesLock = new ReentrantLock();@b@ map = new ConcurrentHashMap<K, SoftValue<V, K>>();@b@ strongReferences = new ConcurrentLinkedQueue<V>();@b@ }@b@@b@ /**@b@ * Creates a {@code SoftHashMap} backed by the specified {@code source}, with a default retention@b@ * size of {@link #DEFAULT_RETENTION_SIZE DEFAULT_RETENTION_SIZE} (100 entries).@b@ *@b@ * @param source the backing map to populate this {@code SoftHashMap}@b@ * @see #SoftHashMap(Map,int)@b@ */@b@ public SoftHashMap(Map<K, V> source) {@b@ this(DEFAULT_RETENTION_SIZE);@b@ putAll(source);@b@ }@b@@b@ /**@b@ * Creates a {@code SoftHashMap} backed by the specified {@code source}, with the specified retention size.@b@ * <p/>@b@ * The retention size (n) is the total number of most recent entries in the map that will be strongly referenced@b@ * (ie 'retained') to prevent them from being eagerly garbage collected. That is, the point of a SoftHashMap is to@b@ * allow the garbage collector to remove as many entries from this map as it desires, but there will always be (n)@b@ * elements retained after a GC due to the strong references.@b@ * <p/>@b@ * Note that in a highly concurrent environments the exact total number of strong references may differ slightly@b@ * than the actual <code>retentionSize</code> value. This number is intended to be a best-effort retention low@b@ * water mark.@b@ *@b@ * @param source the backing map to populate this {@code SoftHashMap}@b@ * @param retentionSize the total number of most recent entries in the map that will be strongly referenced@b@ * (retained), preventing them from being eagerly garbage collected by the JVM.@b@ */@b@ public SoftHashMap(Map<K, V> source, int retentionSize) {@b@ this(retentionSize);@b@ putAll(source);@b@ }@b@@b@ public V get(Object key) {@b@ processQueue();@b@@b@ V result = null;@b@ SoftValue<V, K> value = map.get(key);@b@@b@ if (value != null) {@b@ //unwrap the 'real' value from the SoftReference@b@ result = value.get();@b@ if (result == null) {@b@ //The wrapped value was garbage collected, so remove this entry from the backing map:@b@ //noinspection SuspiciousMethodCalls@b@ map.remove(key);@b@ } else {@b@ //Add this value to the beginning of the strong reference queue (FIFO).@b@ addToStrongReferences(result);@b@ }@b@ }@b@ return result;@b@ }@b@@b@ private void addToStrongReferences(V result) {@b@ strongReferencesLock.lock();@b@ try {@b@ strongReferences.add(result);@b@ trimStrongReferencesIfNecessary();@b@ } finally {@b@ strongReferencesLock.unlock();@b@ }@b@@b@ }@b@@b@ //Guarded by the strongReferencesLock in the addToStrongReferences method@b@@b@ private void trimStrongReferencesIfNecessary() {@b@ //trim the strong ref queue if necessary:@b@ while (strongReferences.size() > RETENTION_SIZE) {@b@ strongReferences.poll();@b@ }@b@ }@b@@b@ /**@b@ * Traverses the ReferenceQueue and removes garbage-collected SoftValue objects from the backing map@b@ * by looking them up using the SoftValue.key data member.@b@ */@b@ private void processQueue() {@b@ SoftValue sv;@b@ while ((sv = (SoftValue) queue.poll()) != null) {@b@ //noinspection SuspiciousMethodCalls@b@ map.remove(sv.key); // we can access private data!@b@ }@b@ }@b@@b@ public boolean isEmpty() {@b@ processQueue();@b@ return map.isEmpty();@b@ }@b@@b@ public boolean containsKey(Object key) {@b@ processQueue();@b@ return map.containsKey(key);@b@ }@b@@b@ public boolean containsValue(Object value) {@b@ processQueue();@b@ Collection values = values();@b@ return values != null && values.contains(value);@b@ }@b@@b@ public void putAll(Map<? extends K, ? extends V> m) {@b@ if (m == null || m.isEmpty()) {@b@ processQueue();@b@ return;@b@ }@b@ for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {@b@ put(entry.getKey(), entry.getValue());@b@ }@b@ }@b@@b@ public Set<K> keySet() {@b@ processQueue();@b@ return map.keySet();@b@ }@b@@b@ public Collection<V> values() {@b@ processQueue();@b@ Collection<K> keys = map.keySet();@b@ if (keys.isEmpty()) {@b@ //noinspection unchecked@b@ return Collections.EMPTY_SET;@b@ }@b@ Collection<V> values = new ArrayList<V>(keys.size());@b@ for (K key : keys) {@b@ V v = get(key);@b@ if (v != null) {@b@ values.add(v);@b@ }@b@ }@b@ return values;@b@ }@b@@b@ /**@b@ * Creates a new entry, but wraps the value in a SoftValue instance to enable auto garbage collection.@b@ */@b@ public V put(K key, V value) {@b@ processQueue(); // throw out garbage collected values first@b@ SoftValue<V, K> sv = new SoftValue<V, K>(value, key, queue);@b@ SoftValue<V, K> previous = map.put(key, sv);@b@ addToStrongReferences(value);@b@ return previous != null ? previous.get() : null;@b@ }@b@@b@ public V remove(Object key) {@b@ processQueue(); // throw out garbage collected values first@b@ SoftValue<V, K> raw = map.remove(key);@b@ return raw != null ? raw.get() : null;@b@ }@b@@b@ public void clear() {@b@ strongReferencesLock.lock();@b@ try {@b@ strongReferences.clear();@b@ } finally {@b@ strongReferencesLock.unlock();@b@ }@b@ processQueue(); // throw out garbage collected values@b@ map.clear();@b@ }@b@@b@ public int size() {@b@ processQueue(); // throw out garbage collected values first@b@ return map.size();@b@ }@b@@b@ public Set<Map.Entry<K, V>> entrySet() {@b@ processQueue(); // throw out garbage collected values first@b@ Collection<K> keys = map.keySet();@b@ if (keys.isEmpty()) {@b@ //noinspection unchecked@b@ return Collections.EMPTY_SET;@b@ }@b@@b@ Map<K, V> kvPairs = new HashMap<K, V>(keys.size());@b@ for (K key : keys) {@b@ V v = get(key);@b@ if (v != null) {@b@ kvPairs.put(key, v);@b@ }@b@ }@b@ return kvPairs.entrySet();@b@ }@b@@b@ /**@b@ * We define our own subclass of SoftReference which contains@b@ * not only the value but also the key to make it easier to find@b@ * the entry in the HashMap after it's been garbage collected.@b@ */@b@ private static class SoftValue<V, K> extends SoftReference<V> {@b@@b@ private final K key;@b@@b@ /**@b@ * Constructs a new instance, wrapping the value, key, and queue, as@b@ * required by the superclass.@b@ *@b@ * @param value the map value@b@ * @param key the map key@b@ * @param queue the soft reference queue to poll to determine if the entry had been reaped by the GC.@b@ */@b@ private SoftValue(V value, K key, ReferenceQueue<? super V> queue) {@b@ super(value, queue);@b@ this.key = key;@b@ }@b@@b@ }@b@}