文章出處

閑來無事,用Java的軟引用寫了一個山寨的緩存

博客分類:
  • java基礎
眾所周知java中的引用分為 StrongReference、SoftReference、WeakReference、PhantomReference。這幾種引用有不同那個的 使用場景,平時我們用的最頻繁的也就是StrongReference也就是說形如之這樣的引用:

 

Object obj = new Object();

  這種引用就是所謂的強引用,如果此對象沒有引用指向它,并且活著的線程無法訪問到它(針對垃圾孤島而言),那么他才會被回收,如果該對象被強引用指向,并且內存被耗盡,拋出OOM垃圾收集器也不會回收該對象。

而對于SoftReference而言它被GC回收的條件就沒 那么嚴格了,如果一個對象當前最強的引用是軟引用,并且JVM的內存充足,垃圾回收器是不會回收的該對象的。只有在內存比較吃緊的情況下GC才會回收被軟 引用指向的對象,從這個特點我們就看出了軟引用可以用來做一些高速緩存,配合LRU策略更好。今天我就自己寫一個玩玩。

 

Java代碼  收藏代碼
  1. 代碼如下:  
Java代碼  收藏代碼
/* 
 @author blackbeans
 *  
 * 背景知識: 
 *          而對于SoftReference而言它被GC回收的條件就沒那么嚴格了, 
 *          如果一個對象當前最強的引用是軟引用,并且JVM的內存充足, 
 *          垃圾回收器是不會回收的該對象的。只有在內存比較吃緊的情況下GC才會回收被軟引用指向的對象, 
 *          從這個特點我們就看出了軟引用可以用來做一些高速緩存,配合LRU策略更好。今天我就自己寫一個玩玩。 
 *  
 *          而對于SoftReference而言它被GC回收的條件就沒那么嚴格了, 
 *          如果一個對象當前最強的引用是軟引用,并且JVM的內存充足, 
 *          垃圾回收器是不會回收的該對象的。 
 *          只有在內存比較吃緊的情況下GC才會回收被軟引用指向的對象, 
 *          從這個特點我們就看出了軟引用可以用來做一些高速緩存,配合LRU策略更好。 
 *  
 */
public class ReferenceCache<K, T> {

    private HashMap<K, InnerReference<K, T>> cachedReference = new HashMap<K, InnerReference<K, T>>(1024);

    private final ReferenceQueue<T> referenceQueue;

    private final ObjectNotFoundHandler<K, T> existsHandler;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 緩存中取不到時的處理器 接口
     *
     * @author blackbeans
     *
     * @param <K>
     * @param <T>
     */
    public static interface ObjectNotFoundHandler<K, T> {

        public T queryAndCached(K key);
    }

    /**
     * 默認緩存中取不到時的處理器 實現類
     *
     * @author blackbeans
     *
     * @param <K>
     * @param <T>
     */
    private static class DefaultObjectNotFoundHandler<K, T> implements ObjectNotFoundHandler<K, T> {

        @Override
        public T queryAndCached(K key) {
            return null;
        }

    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 繼承軟引用
     *
     * @param <K>
     * @param <T>
     */
    private static class InnerReference<K, T> extends SoftReference<T> {

        private final K key;

        public InnerReference(K key, T reference, ReferenceQueue<T> queue) {
            super(reference, queue);
            this.key = key;
        }

        public K getKey() {
            return this.key;
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /**
     * 本類構造方法
     *
     * @param handler
     */
    public ReferenceCache(ObjectNotFoundHandler<K, T> handler) {
        this.referenceQueue = new ReferenceQueue<T>();
        this.existsHandler = handler == null ? new DefaultObjectNotFoundHandler<K, T>() : handler;
    }

    public ReferenceCache() {
        this(null);
    }

    /**
     * 清除給定key的緩存
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    private void cleanReference(K key) {
        //優先檢查key對應軟引用的對象是否被回收     
        if (this.cachedReference.containsKey(key) && this.cachedReference.get(key).get() == null) {
            this.cachedReference.remove(key);
        }
        T obj = null;
        //如果當前Key對應的軟引用的對象被回收則移除該Key     
        Reference<? extends T> reference = null;
        while ((reference = this.referenceQueue.poll()) != null) {
            obj = reference.get();
            if (obj == null) {
                this.cachedReference.remove(((InnerReference<K, T>) reference).getKey());
            }
        }
    }

    /**
     * 把需要緩存的數據放入緩存池
     *
     * @param key
     * @param reference
     */
    public void put(K key, T reference) {//cachedReference
        /**
         * 清除被軟引用的對象并已經被回收的reference
         */
        cleanReference(key);
        if (!this.cachedReference.containsKey(key)) {
            this.cachedReference.put(key, new InnerReference<K, T>(key, reference, this.referenceQueue));
        }
    }

    /**
     * 獲取緩存池內的換成對象
     *
     * @param key
     * @return
     */
    public T get(K key) {//getReference

        T obj = null;

        if (this.cachedReference.containsKey(key)) {
            obj = this.cachedReference.get(key).get();
        }

        if (null == obj) {
            /**
             * 軟引用指向的對象被回收,并緩存該軟引用
             */
            obj = this.existsHandler.queryAndCached(key);
            this.put(key, obj);
            return obj;
        }
        return obj;

    }

    /**
     * 清空所有緩存
     */
    public void clearALLObject() {
        this.cachedReference.clear();
        System.gc();
    }

    public boolean containsKey(Object key) {
        return cachedReference.containsKey(key);
    }

    public static void main(String[] args) {
        ReferenceCache cache = new ReferenceCache();
        cache.put("11", new SiteBean());
        System.out.println(cache.get("11"));
        cache.cleanReference("11");
        cache.clearALLObject();
    }

 

 

   在整個實現中通過將對象的引用放入我定義的一個 key->軟引用map中,然后每次從cache中獲取對象時,首先通過key去查詢map獲得對象的軟引用,若存在則通過軟引用去嘗試獲取對象, 若不存在,軟引用指向的對象被回收,那么我們就回去調用內置的handler,重新生成一個對象,并cache該對象的軟引用。

在我的實現中我為用戶提供了一個當對象被回收時的處理handler,企圖來指導用戶通過這個handler來重新構造對象,緩存對象,靈活性還是挺大的。

不足之處就是,如果軟引用的緩存能用LRU策略更完美了,再為 LRU提供一個Processor,用于用戶自定義LRU策略。其實很簡單只要將HashMap換成LinkedHashMap去實現 removeEldest方法,并在方法中調用自定義的LRU處理器就OK了。

        為了減少開銷,我在每次cache的時候才去清理已經失效的軟引用。也許有人會問為啥有個ReferenceQueue呢?其實是這樣的,在軟引用所引用 的對象被回收以后,試想想對象軟引用的對象是被回收了,但是你又引入了另一個對象SoftReference,帶走一個難道還要再留下一個,所以不會的, 軟引用對象被回收后,這個軟引用本身被添加到了這個queue,等待回收。通過便利這個queue獲取軟引用來一出map中過期的軟引用。

至此,該說的也都說了,不該說的也說了,結尾很突兀,敬請見諒!

 

轉載:http://blackbeans.iteye.com/blog/1039464


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()