用Java的软引用写一个山寨的缓存

众所周知java中的引用分为StrongReference、SoftReference、WeakReference、PhantomReference。这几种引用有不同那个的使用场景,平时我们用的最频繁的也就是StrongReference也就是说形...

众所周知java中的引用分为StrongReference、SoftReference、WeakReference、PhantomReference。这几种引用有不同那个的使用场景,平时我们用的最频繁的也就是StrongReference也就是说形如之这样的引用:

  
 Object obj = new Object();

  这种引用就是所谓的强引用,如果此对象没有引用指向它,并且活着的线程无法访问到它(针对垃圾孤岛而言),那么他才会被回收,如果该对象被强引用指向,并且内存被耗尽,抛出OOM垃圾收集器也不会回收该对象。

 而对于SoftReference而言它被GC回收的条件就没那么严格了,如果一个对象当前最强的引用是软引用,并且JVM的内存充足,垃圾回收器是不会回收的该对象的。只有在内存比较吃紧的情况下GC才会回收被软引用指向的对象,从这个特点我们就看出了软引用可以用来做一些高速缓存,配合LRU策略更好。今天我就自己写一个玩玩。



package 第9天.JAVA_4_种引用类型;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;


/*
 * 用Java的软引用写一个山寨的缓存
 * 
 * 背景知识:
 * 			而对于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) {   
	            // TODO Auto-generated method stub   
	            return null;   
	        }   
	           
	    }   
	       
	   
	       
	       
	    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;   
	        }   
	    }    
	       
	       
	    public ReferenceCache(ObjectNotFoundHandler<K,T> handler)   
	    {   
	        this.referenceQueue = new ReferenceQueue<T>();   
	        this.existsHandler = handler == null ? new DefaultObjectNotFoundHandler<K,T>() : handler;   
	    }   
	       
	       
	    public ReferenceCache()   
	    {   
	        this(null);   
	    }   
	    
	    
	    @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());   
	            }   
	        }   
	    }   
	       
	    public void cachedReference(K key,T reference)   
	    {   
	        /**  
	         * 清除被软引用的对象并已经被回收的reference  
	         */  
	        cleanReference(key);   
	        if(!this.cachedReference.containsKey(key))   
	        {   
	            this.cachedReference.put(key, new InnerReference<K,T>(key,reference, this.referenceQueue));   
	        }   
	    }   
	       
	    public T getReference(K key)    {   
	           
	        T obj = null;   
	           
	        if(this.cachedReference.containsKey(key)){   
	            obj =  this.cachedReference.get(key).get();   
	        }   
	           
	        if(null == obj)   
	        {   
	            /**  
	             * 软引用指向的对象被回收,并缓存该软引用  
	             */  
	            obj = this.existsHandler.queryAndCached(key);   
	            this.cachedReference(key, obj);   
	            return obj;   
	        }   
	        return obj;   
	           
	    }   
	  
	   
	       
	       
	    public void clearALLObject()   
	    {   
	        this.cachedReference.clear();   
	        System.gc();   
	    }   
	       
	

}

在整个实现中通过将对象的引用放入我定义的一个key->软引用map中,然后每次从cache中获取对象时,首先通过key去查询map获得对象的软引用,若存在则通过软引用去尝试获取对象,若不存在,软引用指向的对象被回收,那么我们就回去调用内置的handler,重新生成一个对象,并cache该对象的软引用。

 在我的实现中我为用户提供了一个当对象被回收时的处理handler,企图来指导用户通过这个handler来重新构造对象,缓存对象,灵活性还是挺大的。

 不足之处就是,如果软引用的缓存能用LRU策略更完美了,再为LRU提供一个Processor,用于用户自定义LRU策略。其实很简单只要将HashMap换成LinkedHashMap去实现removeEldest方法,并在方法中调用自定义的LRU处理器就OK了。

        为了减少开销,我在每次cache的时候才去清理已经失效的软引用。也许有人会问为啥有个ReferenceQueue呢?其实是这样的,在软引用所引用的对象被回收以后,试想想对象软引用的对象是被回收了,但是你又引入了另一个对象SoftReference,带走一个难道还要再留下一个,所以不会的,软引用对象被回收后,这个软引用本身被添加到了这个queue,等待回收。通过便利这个queue获取软引用来一出map中过期的软引用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值