Java实现一个简单的缓存方法
程序员文章站
2024-03-01 12:28:58
缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率。下面我实现了一...
缓存是在web开发中经常用到的,将程序经常使用到或调用到的对象存在内存中,或者是耗时较长但又不具有实时性的查询数据放入内存中,在一定程度上可以提高性能和效率。下面我实现了一个简单的缓存,步骤如下。
创建缓存对象entitycache.java
public class entitycache { /** * 保存的数据 */ private object datas; /** * 设置数据失效时间,为0表示永不失效 */ private long timeout; /** * 最后刷新时间 */ private long lastrefeshtime; public entitycache(object datas, long timeout, long lastrefeshtime) { this.datas = datas; this.timeout = timeout; this.lastrefeshtime = lastrefeshtime; } public object getdatas() { return datas; } public void setdatas(object datas) { this.datas = datas; } public long gettimeout() { return timeout; } public void settimeout(long timeout) { this.timeout = timeout; } public long getlastrefeshtime() { return lastrefeshtime; } public void setlastrefeshtime(long lastrefeshtime) { this.lastrefeshtime = lastrefeshtime; } }
定义缓存操作接口,icachemanager.java
public interface icachemanager { /** * 存入缓存 * @param key * @param cache */ void putcache(string key, entitycache cache); /** * 存入缓存 * @param key * @param cache */ void putcache(string key, object datas, long timeout); /** * 获取对应缓存 * @param key * @return */ entitycache getcachebykey(string key); /** * 获取对应缓存 * @param key * @return */ object getcachedatabykey(string key); /** * 获取所有缓存 * @param key * @return */ map<string, entitycache> getcacheall(); /** * 判断是否在缓存中 * @param key * @return */ boolean iscontains(string key); /** * 清除所有缓存 */ void clearall(); /** * 清除对应缓存 * @param key */ void clearbykey(string key); /** * 缓存是否超时失效 * @param key * @return */ boolean istimeout(string key); /** * 获取所有key * @return */ set<string> getallkeys(); }
实现接口icachemanager,cachemanagerimpl.java
这里我使用了concurrenthashmap来保存缓存,本来以为这样就是线程安全的,其实不然,在后面的测试中会发现它并不是线程安全的。
public class cachemanagerimpl implements icachemanager { private static map<string, entitycache> caches = new concurrenthashmap<string, entitycache>(); /** * 存入缓存 * @param key * @param cache */ public void putcache(string key, entitycache cache) { caches.put(key, cache); } /** * 存入缓存 * @param key * @param cache */ public void putcache(string key, object datas, long timeout) { timeout = timeout > 0 ? timeout : 0l; putcache(key, new entitycache(datas, timeout, system.currenttimemillis())); } /** * 获取对应缓存 * @param key * @return */ public entitycache getcachebykey(string key) { if (this.iscontains(key)) { return caches.get(key); } return null; } /** * 获取对应缓存 * @param key * @return */ public object getcachedatabykey(string key) { if (this.iscontains(key)) { return caches.get(key).getdatas(); } return null; } /** * 获取所有缓存 * @param key * @return */ public map<string, entitycache> getcacheall() { return caches; } /** * 判断是否在缓存中 * @param key * @return */ public boolean iscontains(string key) { return caches.containskey(key); } /** * 清除所有缓存 */ public void clearall() { caches.clear(); } /** * 清除对应缓存 * @param key */ public void clearbykey(string key) { if (this.iscontains(key)) { caches.remove(key); } } /** * 缓存是否超时失效 * @param key * @return */ public boolean istimeout(string key) { if (!caches.containskey(key)) { return true; } entitycache cache = caches.get(key); long timeout = cache.gettimeout(); long lastrefreshtime = cache.getlastrefeshtime(); if (timeout == 0 || system.currenttimemillis() - lastrefreshtime >= timeout) { return true; } return false; } /** * 获取所有key * @return */ public set<string> getallkeys() { return caches.keyset(); } }
cachelistener.java,监听失效数据并移除。
public class cachelistener{ logger logger = logger.getlogger("cachelog"); private cachemanagerimpl cachemanagerimpl; public cachelistener(cachemanagerimpl cachemanagerimpl) { this.cachemanagerimpl = cachemanagerimpl; } public void startlisten() { new thread(){ public void run() { while (true) { for(string key : cachemanagerimpl.getallkeys()) { if (cachemanagerimpl.istimeout(key)) { cachemanagerimpl.clearbykey(key); logger.info(key + "缓存被清除"); } } } } }.start(); } }
测试类testcache.java
public class testcache { logger logger = logger.getlogger("cachelog"); /** * 测试缓存和缓存失效 */ @test public void testcachemanager() { cachemanagerimpl cachemanagerimpl = new cachemanagerimpl(); cachemanagerimpl.putcache("test", "test", 10 * 1000l); cachemanagerimpl.putcache("mytest", "mytest", 15 * 1000l); cachelistener cachelistener = new cachelistener(cachemanagerimpl); cachelistener.startlisten(); logger.info("test:" + cachemanagerimpl.getcachebykey("test").getdatas()); logger.info("mytest:" + cachemanagerimpl.getcachebykey("mytest").getdatas()); try { timeunit.seconds.sleep(20); } catch (interruptedexception e) { e.printstacktrace(); } logger.info("test:" + cachemanagerimpl.getcachebykey("test")); logger.info("mytest:" + cachemanagerimpl.getcachebykey("mytest")); } /** * 测试线程安全 */ @test public void testthredsafe() { final string key = "thread"; final cachemanagerimpl cachemanagerimpl = new cachemanagerimpl(); executorservice exec = executors.newcachedthreadpool(); for (int i = 0; i < 100; i++) { exec.execute(new runnable() { public void run() { if (!cachemanagerimpl.iscontains(key)) { cachemanagerimpl.putcache(key, 1, 0); } else { //因为+1和赋值操作不是原子性的,所以把它用synchronize块包起来 synchronized (cachemanagerimpl) { int value = (integer) cachemanagerimpl.getcachedatabykey(key) + 1; cachemanagerimpl.putcache(key,value , 0); } } } }); } exec.shutdown(); try { exec.awaittermination(1, timeunit.days); } catch (interruptedexception e1) { e1.printstacktrace(); } logger.info(cachemanagerimpl.getcachedatabykey(key).tostring()); } }
testcachemanager()输出结果如下:
2017-4-17 10:33:51 io.github.brightloong.cache.testcache testcachemanager 信息: test:test 2017-4-17 10:33:51 io.github.brightloong.cache.testcache testcachemanager 信息: mytest:mytest 2017-4-17 10:34:01 io.github.brightloong.cache.cachelistener$1 run 信息: test缓存被清除 2017-4-17 10:34:06 io.github.brightloong.cache.cachelistener$1 run 信息: mytest缓存被清除 2017-4-17 10:34:11 io.github.brightloong.cache.testcache testcachemanager 信息: test:null 2017-4-17 10:34:11 io.github.brightloong.cache.testcache testcachemanager 信息: mytest:null
testthredsafe()输出结果如下(选出了各种结果中的一个举例):
2017-4-17 10:35:36 io.github.brightloong.cache.testcache testthredsafe 信息: 96
可以看到并不是预期的结果100,为什么呢?concurrenthashmap只能保证单次操作的原子性,但是当复合使用的时候,没办法保证复合操作的原子性,以下代码:
if (!cachemanagerimpl.iscontains(key)) { cachemanagerimpl.putcache(key, 1, 0); }
多线程的时候回重复更新value,设置为1,所以出现结果不是预期的100。所以办法就是在cachemanagerimpl.java中都加上synchronized,但是这样一来相当于操作都是串行,使用concurrenthashmap也没有什么意义,不过只是简单的缓存还是可以的。或者对测试方法中的run里面加上synchronized块也行,都是大同小异。更高效的方法我暂时也想不出来,希望大家能多多指教。