`
blackbeans
  • 浏览: 141761 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

闲来无事,用Java的软引用写了一个山寨的缓存

阅读更多

今天杭州的天气是异常的炎热,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中过期的软引用。

至此,该说的也都说了,不该说的也说了,结尾很突兀,敬请见谅!

 

 

分享到:
评论
23 楼 blackbeans 2011-05-12  
snwz 写道
您好 我想请教下
在第七行代码
# if(obj == null) 
#             { 
#                 this.cachedReference.remove(((InnerReference<K, T>)reference).getKey()); 
#             } 

为什么要在obj为空时候才remove?
还有就是while 循环里ref = this.queue.poll()  怎么就能确定获取的就是当前要删除的 ref呢?


这个跟gc有关,对于软引用当gc去回收软引用所引用的对象同时,也会去将软引用本身放到所谓的referceQueue中,来回收,不然虽然回收了软引用所引用的对象,但是产生了额外的SoftReference的垃圾
22 楼 snwz 2011-05-12  
您好 我想请教下
在第七行代码
# if(obj == null) 
#             { 
#                 this.cachedReference.remove(((InnerReference<K, T>)reference).getKey()); 
#             } 

为什么要在obj为空时候才remove?
还有就是while 循环里ref = this.queue.poll()  怎么就能确定获取的就是当前要删除的 ref呢?
21 楼 blackbeans 2011-05-12  
fmjsjx 写道
为什么要用HashMap呢?按照楼主用softReference的思路,用WeakHashMap不更合适么。
而且楼主好像也没处理并发问题,至少要加个读写锁才能用吧……

写来玩玩而已,并发的方面我没有考虑,只是个想法,提供基本的cache功能,然后提供一个Handler来处理对象被回收的情况。
20 楼 fmjsjx 2011-05-12  
为什么要用HashMap呢?按照楼主用softReference的思路,用WeakHashMap不更合适么。
而且楼主好像也没处理并发问题,至少要加个读写锁才能用吧……
19 楼 sarstime 2011-05-12  
闲来无事 是亮点。
18 楼 beneo 2011-05-11  
blackbeans 写道
beneo 写道
其实你不知道object reference 何时 enqueue,所以你的代码还是有问题

为什么要关注 合适进去queue里呢?


1. 创建一个 object 并且放入 reference
2. put key, reference, referenceQueue in your cache
3. the object = null, and if the softreference do not enqueue

问题 reference,object = null,此时并没有enqueu, 调用cachedReference,结果本来就出问题了
17 楼 blackbeans 2011-05-11  
何处烤地瓜 写道
你在那里的上班啊?     华星路?   还是滨江

不告诉你哈哈
16 楼 何处烤地瓜 2011-05-11  
你在那里的上班啊?     华星路?   还是滨江
15 楼 blackbeans 2011-05-11  
beneo 写道
其实你不知道object reference 何时 enqueue,所以你的代码还是有问题

为什么要关注 合适进去queue里呢?我只关心,弱引用被放入queue,检查object有没有被回收,被回收就删除映射,就好啦
14 楼 beneo 2011-05-11  
其实你不知道object reference 何时 enqueue,所以你的代码还是有问题
13 楼 blackbeans 2011-05-11  
beneo 写道
this.referenceQueue.poll() 出来的 reference 与你的 key 应该不是对应的,这么写是为什么?

referenceQueue只是一个node-list,而且enqueue顺序与gc有关,LZ如何拿key与reference关联?


代码已经修改了,我将创建软引用的时候就去记录这个Key,遍历整个queue的时候就可以去删除对应的key-SoftReference的映射了
12 楼 blackbeans 2011-05-11  
yeswoyaofei 写道
private void cleanReference(K key) 这个函数没看懂。能解释一下吗


代码已经修改了,之前考虑的不周到,尽请见谅
11 楼 blackbeans 2011-05-11  
kakaluyi 写道
假如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);  
            }  
        }  
    }  



恩这里考虑的不周到,意图是利用ReferenceQueue来断定软引用引用的对象是否被回收来决定删除key到Softreference的映射,看来写的有问题呵呵,感谢提出建议
10 楼 Rooock 2011-05-11  
想法是挺好的. 不过貌似大伙都不喜欢画图说话.
9 楼 beneo 2011-05-11  
this.referenceQueue.poll() 出来的 reference 与你的 key 应该不是对应的,这么写是为什么?

referenceQueue只是一个node-list,而且enqueue顺序与gc有关,LZ如何拿key与reference关联?
8 楼 yeswoyaofei 2011-05-11  
private void cleanReference(K key) 这个函数没看懂。能解释一下吗
7 楼 kakaluyi 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);  
            }  
        }  
    }  
6 楼 olylakers 2011-05-11  
blackbeans 写道
shansun123 写道
hxrs 写道
阿里集体结婚?~

应该是

还是你厉害

看到好几个阿里的朋友抱怨了。。。。
5 楼 blackbeans 2011-05-11  
shansun123 写道
hxrs 写道
阿里集体结婚?~

应该是

还是你厉害
4 楼 shansun123 2011-05-11  
hxrs 写道
阿里集体结婚?~

应该是

相关推荐

    山寨机java游戏

    Java ME(Micro Edition)是Java的一个分支,专门用于开发移动设备应用,包括游戏。 2. **诺基亚手机与Java游戏**:诺基亚是早期智能手机市场的巨头,其S40和S60系统广泛支持Java游戏。尽管诺基亚N8和N9后来采用了...

    JAVA山寨版聊天源代码

    5. **用户界面**:考虑到这是“山寨版QQ”,很可能包含一个图形用户界面(GUI),使用Java Swing或JavaFX库创建。用户可以在这里输入和查看消息。 6. **数据持久化**:如果项目中包含了存储用户信息或聊天记录的...

    山寨java版qq

    【标题】"山寨java版qq" 是一个基于Java编程语言开发的简易聊天软件,它旨在模仿QQ的功能,但并非官方版本。这个项目主要是为了学习和实践网络通信、多线程以及用户界面设计等技术。 【描述】这个"山寨版局域网聊天...

    java版山寨QQ项目源码

    java版山寨QQ项目 里面有四个压缩包 QQ服务器端.rar QQ客户端.rar 改进QQ客户机服务器视频实现.rar Q第三方库.rar 这个java版山寨QQ项目实现的功能有: 1.实现的java视频聊天; 2.集成了Mina框架; 3.服务器管理...

    山寨QQ2003java源码

    这为学习者提供了一个动手实践的机会,他们可以深入理解QQ的运行机制,以及如何使用Java和数据库技术来实现即时通讯功能。 【标签】"qq"、"山寨"、"源码"、"java"明确了这个项目的主题。"qq"表示与腾讯QQ相关,...

    java编写的山寨qq

    本项目是一个基于Java编程语言开发的仿QQ软件,它旨在模拟QQ的主要功能,提供用户界面仿真体验,包括账户验证、实时聊天以及好友状态显示等核心特性。以下是对这个项目的详细解读: 1. **Java语言基础**:Java是一...

    java写的山寨QQ+素材

    使用java语言写的山寨版QQ和素材,是学习java语言的好材料!!!!

    java语言 山寨版QQ源码

    【标题】:“java语言 山寨版QQ源码”指的是基于Java编程语言实现的一个简易QQ聊天软件的源代码。这个项目通常是为了教学目的而设计,让学习者能够深入理解Java编程语言在网络通信、多线程、图形用户界面(GUI)等...

    java实现的山寨版QQ

    【Java实现的山寨版QQ】项目是一个基于Java编程语言开发的即时通讯软件,它旨在模拟腾讯QQ的基本功能,包括用户登录、在线好友列表展示、一对一私聊以及多人聊天室等功能。这个项目对于学习Java GUI设计、网络编程...

    java山寨版qq项目

    【Java山寨版QQ项目】是韩顺平老师为了教学目的设计的一个模拟QQ功能的应用程序,旨在帮助初学者理解和掌握Java编程语言以及相关的软件开发技术。这个项目提供了从基础到进阶的Java编程实践,涵盖了网络通信、多线程...

    Java山寨版QQ

    3. 用户界面:为了模拟QQ的聊天功能,客户端通常需要设计图形用户界面(GUI),可能使用Java Swing或JavaFX库来实现。 此外,为了实现多用户同时在线聊天,服务器端需要处理多个并发连接。这需要用到Java的多线程...

    Java山寨版qq源码+素材

    Java山寨版QQ源码是一个基于Java编程语言实现的简易即时通讯软件项目,旨在为学习者提供一个基础的、可理解的实例,以便更好地理解和掌握Java编程以及网络通信相关的技术。这个项目不仅包含了源代码,还提供了相关的...

    Java实习报告——山寨QQ聊天工具设计

    在设备选择和环境配置方面,报告指出需要安装JDK1.5及以上版本,并设置好环境变量,同时还需要一个像MyEclipse这样的集成开发环境来编写和编译Java代码。 报告提供的部分代码展示了实现这一聊天工具的关键组件: -...

    java山寨qq

    在这个课程中,他通过讲解如何实现一个“山寨QQ”来帮助学员深入理解Java语言的核心概念和技术。这个项目不仅是一个有趣的实践,也是一个学习Java网络编程、多线程、IO流以及图形用户界面设计的绝佳案例。 首先,...

    java源码包---java 源码 大量 实例

    EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,...

    简易的山寨版javaQQ系统

    【简易的山寨版javaQQ系统】是一个基于Java编程语言实现的简单即时通讯软件,它旨在模仿QQ的基本功能,为用户提供一个基础的聊天平台。在这个项目中,我们可以学习到Java编程在构建网络通信应用中的基本原理和技巧。...

    用java编写的山寨QQ

    【标题】"用Java编写的山寨QQ"是一个项目,它旨在模仿腾讯QQ的基本功能,让开发者能够学习和实践Java编程语言以及网络通信技术。这个项目可能是个人或团队为提升编程技能而进行的一个挑战。 【描述】提到“自己用...

    java GUI 山寨QQ

    【标题】"java GUI 山寨QQ"是一个基于Java Standard Edition (Java SE) 开发的图形用户界面(GUI)应用程序,旨在模仿QQ即时通讯软件的功能。这个项目展示了如何利用Java的Swing或JavaFX库来构建桌面应用,并通过SQL...

    Java版山寨星际----下载不扣分,回帖加1分,欢迎下载,童叟无欺

    做了一大堆J2EE WEB应用之后,闲来无事决定研究一下桌面应用程序开发。 想了一下干脆做个Java版的星际,研究的对象就是星际的JS山寨版-JS星际。看了几下,当机立断决定山寨之。。。。 后来经过无数个夜晚的煎熬终于...

    山寨版QQ--存Java实现!!

    本篇文章将围绕一个独特的项目——用Java实现的“山寨版QQ”进行深入探讨,揭示其主要功能的实现原理和技术细节。 首先,这个JavaQQ项目旨在模拟QQ的基本功能,包括用户注册、登录以及头像展示等核心模块。在注册...

Global site tag (gtag.js) - Google Analytics