精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-05-10
最后修改:2011-05-12
今天杭州的天气是异常的炎热,35度,这是要我卿命呢,加之最近心情极其差劲,scala的东西也一段时间没看没写了。今天公司又是有犬吠、又是有人结婚、又是有小孩的哭啼、又是有杭州限电公司空调也关了、又是由于XXX原因登境外网站完全崩溃,因此深刻觉得没有Google的程序员不如去卖红薯。 好了闲话少说进入正题,众所周知java中的引用分为StrongReference、SoftReference、WeakReference、PhantomReference。这几种引用有不同那个的使用场景,平时我们用的最频繁的也就是StrongReference也就是说形如之这样的引用: Object obj = new Object(); 这种引用就是所谓的强引用,如果此对象没有引用指向它,并且活着的线程无法访问到它(针对垃圾孤岛而言),那么他才会被回收,如果该对象被强引用指向,并且内存被耗尽,抛出OOM垃圾收集器也不会回收该对象。 而对于SoftReference而言它被GC回收的条件就没那么严格了,如果一个对象当前最强的引用是软引用,并且JVM的内存充足,垃圾回收器是不会回收的该对象的。只有在内存比较吃紧的情况下GC才会回收被软引用指向的对象,从这个特点我们就看出了软引用可以用来做一些高速缓存,配合LRU策略更好。今天我就自己写一个玩玩。
代码如下: 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); 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中过期的软引用。 至此,该说的也都说了,不该说的也说了,结尾很突兀,敬请见谅!
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-05-11
最后修改:2011-05-11
个人理解出现偏差,已修改
|
|
返回顶楼 | |
发表时间:2011-05-11
agapple 写道 个人理解出现偏差,已修改 什么理解偏差,请指出
|
|
返回顶楼 | |
发表时间:2011-05-11
阿里集体结婚?~
|
|
返回顶楼 | |
发表时间:2011-05-11
hxrs 写道 阿里集体结婚?~
应该是 |
|
返回顶楼 | |
发表时间:2011-05-11
shansun123 写道 hxrs 写道 阿里集体结婚?~
应该是 还是你厉害 |
|
返回顶楼 | |
发表时间:2011-05-11
blackbeans 写道 shansun123 写道 hxrs 写道 阿里集体结婚?~
应该是 还是你厉害 看到好几个阿里的朋友抱怨了。。。。 |
|
返回顶楼 | |
发表时间:2011-05-11
假如key不存在,lz你用poll就强制把queue清空最老的数据,这个是你要的结果吗?
private void cleanReference(K key) { Reference<? extends T> reference = null; while((reference = this.referenceQueue.poll()) != null) { T obj = reference.get(); if(obj == null) { this.cachedReference.remove(key); } } } |
|
返回顶楼 | |
发表时间:2011-05-11
private void cleanReference(K key) 这个函数没看懂。能解释一下吗
|
|
返回顶楼 | |
发表时间:2011-05-11
this.referenceQueue.poll() 出来的 reference 与你的 key 应该不是对应的,这么写是为什么?
referenceQueue只是一个node-list,而且enqueue顺序与gc有关,LZ如何拿key与reference关联? |
|
返回顶楼 | |