vlambda博客
学习文章列表

Java实现一个简单的缓存方法


本篇文章主要介绍了Java实现一个简单的缓存方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧!



缓存是在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


import java.util.Map;import java.util.Set;
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来保存缓存,本来以为这样就是线程安全的,其实不然,在后面的测试中会发现它并不是线程安全的。

import java.util.Map;import java.util.Set;import java.util.concurrent.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,监听失效数据并移除


import java.util.logging.Logger;
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

import org.junit.Test;
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import java.util.logging.Logger;
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()输出结果如下:

1月 04, 2022 7:19:29 下午 TestCache testThredSafe信息: 971月 04, 2022 7:19:29 下午 TestCache testCacheManager信息: test:test1月 04, 2022 7:19:29 下午 TestCache testCacheManager信息: myTest:myTest1月 04, 2022 7:19:29 下午 CacheListener$1 run信息: thread缓存被清除1月 04, 2022 7:19:39 下午 CacheListener$1 run信息: test缓存被清除1月 04, 2022 7:19:44 下午 CacheListener$1 run信息: myTest缓存被清除1月 04, 2022 7:19:49 下午 TestCache testCacheManager信息: test:null1月 04, 2022 7:19:49 下午 TestCache testCacheManager信息: myTest:null


testThredSafe()输出结果如下(选出了各种结果中的一个举例):

1月 04, 2022 7:19:29 下午 TestCache testThredSafe信息: 97

可以看到并不是预期的结果100,为什么呢?ConcurrentHashMap只能保证单次操作的原子性,但是当复合使用的时候,没办法保证复合操作的原子性,以下代码:

if (!cacheManagerImpl.isContains(key)) { cacheManagerImpl.putCache(key, 1, 0); }


多线程的时候回重复更新value,设置为1,所以出现结果不是预期的100。所以办法就是在CacheManagerImpl.java中都加上synchronized,但是这样一来相当于操作都是串行,使用ConcurrentHashMap也没有什么意义,不过只是简单的缓存还是可以的。或者对测试方法中的run里面加上synchronized块也行,都是大同小异。更高效的方法我暂时也想不出来,希望大家能多多指教。


源码获取

https://gitee.com/javanoteany/cache.git



点赞评论+关注!

————————

来源:脚本之家



点击下方名片关注,会有更多Java相关知识,更有Java学习方法、项目源码免费分享

如有帮助, 记得分享、收藏、点赞、在看哦