首页

关于apache-shiro源码包中SoftHashMap实现自定义SoftReference软引用Map的实现源码说明

标签:apache,SoftHashMap,SoftReference,软引用,自定义Map     发布时间:2018-05-24   

一、前言

关于apaceshiro源码包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@}