闲来无事,用Java的软引用写了一个山寨的缓存
博客分类:
- java基础
众所周知java中的引用分为 StrongReference、SoftReference、WeakReference、PhantomReference。这几种引用有不同那个的 使用场景,平时我们用的最频繁的也就是StrongReference也就是说形如之这样的引用:
Object obj = new Object();
这种引用就是所谓的强引用,如果此对象没有引用指向它,并且活着的线程无法访问到它(针对垃圾孤岛而言),那么他才会被回收,如果该对象被强引用指向,并且内存被耗尽,抛出OOM垃圾收集器也不会回收该对象。
而对于SoftReference而言它被GC回收的条件就没 那么严格了,如果一个对象当前最强的引用是软引用,并且JVM的内存充足,垃圾回收器是不会回收的该对象的。只有在内存比较吃紧的情况下GC才会回收被软 引用指向的对象,从这个特点我们就看出了软引用可以用来做一些高速缓存,配合LRU策略更好。今天我就自己写一个玩玩。
Java代码
- 代码如下:
Java代码
- package com.blackbeans.example;
- import java.lang.ref.Reference;
- import java.lang.ref.ReferenceQueue;
- import java.lang.ref.SoftReference;
- import java.util.HashMap;
- /**
- *
- * @author blackbeans
- * @param <K>
- * @param <T>
- */
- 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>
- */
- private static class DefaultObjectNotFoundHandler<K,T> implements ObjectNotFoundHandler<K,T>
- {
- @Override
- public T queryAndCached(K key) {
- // TODO Auto-generated method stub
- return null;
- }
- }
- /**
- * 缓存中取不到时的处理器
- * @author blackbeans
- *
- * @param <K>
- * @param <T>
- */
- public static interface ObjectNotFoundHandler<K,T>
- {
- public T queryAndCached(K key);
- }
- 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);
- }
- 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;
- }
- @SuppressWarnings("unchecked")
- private void cleanReference(K key) {
- //优先检查key对应软引用的对象是否被回收
- if (this.cachedReference.containsKey(key)
- && this.cachedReference.get(key).get() == null)
- this.cachedReference.remove(key);
Java代码
- <span style="white-space: pre;"> </span>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 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中过期的软引用。
至此,该说的也都说了,不该说的也说了,结尾很突兀,敬请见谅!
转载:http://blackbeans.iteye.com/blog/1039464