缓存(Cache)就是一块用来存放数据的内存空间。主要作用是暂存数据处理结果,并提供下次访问使用。
缓存的使用非常普遍,比如,浏览器都会在本地缓存页面,从而减少HTTP 的访问次数。又如服务器系统开发时,设计人员为一些核心的 API 加上缓存,从而提高系统的缓存时间。
最简单的缓存实现可以使用 HashMap 。当然,这样做会有很多问题,如何时清理无效的数据;如何防止缓存数据过多而导致内存溢出等。一个稍好的方案是使用 WeakHashMap,使用弱引用维护一张哈希表,而且可以在内存不足的时候清理数据。
但是作为最专业的实现,就应该有一套专业的缓存框架。比如 EHCache、OSCache 和 JBossCache 等。EHCache 缓存出自 Hibernate, 是 Hibernate 框架默认的数据缓存解决方案;OSCache 是由 OpenSympthony 设计的,可以用来缓存任何对象,甚至是缓存部分 JSP 页面或者 HTTP 请求;JBossCache 是由 JBoss 开发、可用于 JBoss 集群间数据共享的缓存框架。
下面,我们以 EHCache 为例,简单介绍一下缓存的基本使用方法。
首先,我们需要在官网 http://ehcache.org 下载 EHCache 包 ,下载完成之后,将 lib 文件夹下的 jar 包添加到工程中就可以使用了。
引入 jar 包之后,在工程的 Classpath 路径下新建一个名为 ehcache.xml 的文件,如上图所示,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<diskStore path="data/ehcache" />
<defaultCache maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap" />
</defaultCache>
<cache name="sampleCache1" maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000" eternal="false" diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU" transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
<cache name="sampleCache2" maxEntriesLocalHeap="1000" eternal="true"
memoryStoreEvictionPolicy="FIFO" />
</ehcache>配置文件中主要的参数及含义如下:
必要属性有3个,
maxEntriesLocalHeap:堆内存中最大缓存对象数,值为0则没有限制
maxEntriesLocalDisk:磁盘中的最大对象数,默认为0不限制
eternal:elements是否永久有效,如果为true,timeouts将被忽略,element将永不过期
以下是可选属性
timeToIdleSeconds:失效前的空闲秒数,当eternal为false时,这个属性才有效,0为不限制
timeToLiveSeconds:失效前的存活秒数,创建时间到失效时间的间隔为存活时间,当eternal为false时,这个属性才有效,0为不限制
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
clearOnFlush:当调用flush()是否清除缓存,默认是
memoryStoreEvictionPolicy:内存回收策略,默认回收策略:最近最少使用Least Recently Used,先进先出First In First Out,Less Frequently Used使用频率最低。localTempSwap 则在缓存数目多的时候交换到磁盘上。
timeToIdleSeconds:如果不是永久存储的缓存,那么在 timeToIdleSeconds 指定时间内没有访问一个条目,则移除它
diskPersistent: 磁盘中的条目是否永久保存
diskExpiryThreadIntervalSeconds:清理缓存的线程运行的时间间隔
transactionalMode:设置缓存动作的事务模式
然后我们在 Java 程序中使用预先定义的缓存。
package bupt.xiaoye.charpter2.ehcache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
public class EhCacheTest {
public static void main(String[] args) throws InterruptedException {
CacheManager manager = CacheManager.create();
// 取出所有的cacheName
String names[] = manager.getCacheNames();
System.out.println("----all cache names----");
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
System.out.println("----------------------");
// 得到一个cache对象
Cache cache1 = manager.getCache(names[0]);
// 向cache1对象里添加缓存
cache1.put(new Element("key1", "values1"));
Element element = cache1.get("key1");
// 读取缓存
System.out.println("key1 \t= " + element.getObjectValue());
// 手动创建一个cache(ehcache里必须有defaultCache存在,"test"可以换成任何值)
Cache cache2 = new Cache("test", 1, true, false, 2, 3);
manager.addCache(cache2);
cache2.put(new Element("jimmy", "菩提树下的杨过"));
// 故意停1.5秒,以验证是否过期
Thread.sleep(1500);
Element eleJimmy = cache2.get("jimmy");
//1.5s < 2s 不会过期
if (eleJimmy != null) {
System.out.println("jimmy \t= " + eleJimmy.getObjectValue());
}
//再等上0.5s, 总时长:1.5 + 0.5 >= min(2,3),过期
Thread.sleep(500);
eleJimmy = cache2.get("jimmy");
if (eleJimmy == null) {
System.out.println("jimmy \t= null" );
}
// 取出一个不存在的缓存项
System.out.println("fake \t= " + cache2.get("fake"));
manager.shutdown();
}
}package bupt.xiaoye.charpter2.ehcache;
import java.io.Serializable;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
public class EHCacheUtil {
private static CacheManager manager;
static {
try {
manager = CacheManager.create();
} catch (CacheException e) {
e.printStackTrace();
}
}
public static void put(String cachename, Serializable key,
Serializable value) {
manager.getCache(cachename).put(new Element(key, value));
}
public static Object get(String cachename, Serializable key) {
try {
Element e = manager.getCache(cachename).get(key);
if (e == null)
return null;
return e.getObjectValue();
} catch (IllegalStateException e) {
e.printStackTrace();
}
return null;
}
}
有了以上工具类,便可以很方便的在实际工作中使用EHCache。
在方法加入缓存的时候,可以使用最原始的编码方式,根据传入的参数构造key,然后去缓存中查找结果。这种实现方式的好处是代码比较直白,简单,缺点是缓存中间和业务层代码紧密耦合,依赖性强。
下面我们介绍基于动态代理的缓存解决方案。基于动态代理的缓存方案的最大好处是,在业务层,无需关注对缓存的操作,缓存操作代码被完全独立并隔离,并且对一个新的函数方法加入缓存不会影响原有的方法实现,是一种非常灵活的软件结构。
最大的好处便是:无需修改一个逻辑方法的代码,便可以为它加上缓存功能,提高其效率。
假如有下面一个方法,它用于对一个整数做因式分解。为这个类创建动态代理,并测试两者的性能:
package bupt.xiaoye.charpter2.ehcache;
import java.io.Serializable;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class HeavyMethodDemo{
public String heavyMethod(int num) throws Exception{
StringBuffer sb = new StringBuffer();
// do something whith num
Thread.sleep(20);
return sb.toString();
}
}
public class CglibHeavyMethodInterceptor implements MethodInterceptor {
HeavyMethodDemo real = new HeavyMethodDemo();
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
String v = (String) EHCacheUtil.get("sampleCache1", (Serializable) arg2[0]);
if (v == null) {
v = real.heavyMethod((Integer) arg2[0]);
EHCacheUtil.put("sampleCache1", (Integer) arg2[0], v);
}
return null;
}
/**
* 带有缓存功能的代理类
* @return
*/
public static HeavyMethodDemo newCachedHeavyMethod() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HeavyMethodDemo.class);
enhancer.setCallback(new CglibHeavyMethodInterceptor());
HeavyMethodDemo cglibProxy = (HeavyMethodDemo) enhancer.create();
return cglibProxy;
}
/**
* 不带缓存功能的主题
* @return
*/
public static HeavyMethodDemo newHeavyMethod(){
return new HeavyMethodDemo();
}
public static void main(String[] args)throws Exception{
HeavyMethodDemo m =newCachedHeavyMethod();
long begin = System.currentTimeMillis();
for(int i=0;i<100;i++){
m.heavyMethod(21474586);
}
System.out.println(System.currentTimeMillis()-begin);
m = newHeavyMethod();
begin = System.currentTimeMillis();
for(int i=0;i<100;i++){
m.heavyMethod(21474586);
}
System.out.println(System.currentTimeMillis()-begin);
}
}
原文地址:http://blog.csdn.net/zq602316498/article/details/39091639