Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的緩存方法
緩存是在web開(kāi)發(fā)中經(jīng)常用到的,將程序經(jīng)常使用到或調(diào)用到的對(duì)象存在內(nèi)存中,或者是耗時(shí)較長(zhǎng)但又不具有實(shí)時(shí)性的查詢數(shù)據(jù)放入內(nèi)存中,在一定程度上可以提高性能和效率。下面我實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的緩存,步驟如下。
創(chuàng)建緩存對(duì)象EntityCache.java
public class EntityCache {
/**
* 保存的數(shù)據(jù)
*/
private Object datas;
/**
* 設(shè)置數(shù)據(jù)失效時(shí)間,為0表示永不失效
*/
private long timeOut;
/**
* 最后刷新時(shí)間
*/
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);
/**
* 獲取對(duì)應(yīng)緩存
* @param key
* @return
*/
EntityCache getCacheByKey(String key);
/**
* 獲取對(duì)應(yīng)緩存
* @param key
* @return
*/
Object getCacheDataByKey(String key);
/**
* 獲取所有緩存
* @param key
* @return
*/
Map<String, EntityCache> getCacheAll();
/**
* 判斷是否在緩存中
* @param key
* @return
*/
boolean isContains(String key);
/**
* 清除所有緩存
*/
void clearAll();
/**
* 清除對(duì)應(yīng)緩存
* @param key
*/
void clearByKey(String key);
/**
* 緩存是否超時(shí)失效
* @param key
* @return
*/
boolean isTimeOut(String key);
/**
* 獲取所有key
* @return
*/
Set<String> getAllKeys();
}
實(shí)現(xiàn)接口ICacheManager,CacheManagerImpl.java
這里我使用了ConcurrentHashMap來(lái)保存緩存,本來(lái)以為這樣就是線程安全的,其實(shí)不然,在后面的測(cè)試中會(huì)發(fā)現(xiàn)它并不是線程安全的。
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()));
}
/**
* 獲取對(duì)應(yīng)緩存
* @param key
* @return
*/
public EntityCache getCacheByKey(String key) {
if (this.isContains(key)) {
return caches.get(key);
}
return null;
}
/**
* 獲取對(duì)應(yīng)緩存
* @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();
}
/**
* 清除對(duì)應(yīng)緩存
* @param key
*/
public void clearByKey(String key) {
if (this.isContains(key)) {
caches.remove(key);
}
}
/**
* 緩存是否超時(shí)失效
* @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,失效數(shù)據(jù)并移除。
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();
}
}
測(cè)試類(lèi)TestCache.java
public class TestCache {
Logger logger = Logger.getLogger("cacheLog");
/**
* 測(cè)試緩存和緩存失效
*/
@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"));
}
/**
* 測(cè)試線程安全
*/
@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 {
//因?yàn)?1和賦值操作不是原子性的,所以把它用synchronize塊包起來(lái)
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()輸出結(jié)果如下:
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()輸出結(jié)果如下(選出了各種結(jié)果中的一個(gè)舉例):
2017-4-17 10:35:36 io.github.brightloong.cache.TestCache testThredSafe 信息: 96
可以看到并不是預(yù)期的結(jié)果100,為什么呢?ConcurrentHashMap只能保證單次操作的原子性,但是當(dāng)復(fù)合使用的時(shí)候,沒(méi)辦法保證復(fù)合操作的原子性,以下代碼:
if (!cacheManagerImpl.isContains(key)) {
cacheManagerImpl.putCache(key, 1, 0);
}多線程的時(shí)候回重復(fù)更新value,設(shè)置為1,所以出現(xiàn)結(jié)果不是預(yù)期的100。所以辦法就是在CacheManagerImpl.java中都加上synchronized,但是這樣一來(lái)相當(dāng)于操作都是串行,使用ConcurrentHashMap也沒(méi)有什么意義,不過(guò)只是簡(jiǎn)單的緩存還是可以的。或者對(duì)測(cè)試方法中的run里面加上synchronized塊也行,都是大同小異。更高效的方法我暫時(shí)也想不出來(lái),希望大家能多多指教。
相關(guān)文章
dockerfile-maven-plugin極簡(jiǎn)教程(推薦)
這篇文章主要介紹了dockerfile-maven-plugin極簡(jiǎn)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
Spring中@Autowired和@Resource注解相同點(diǎn)和不同點(diǎn)
這篇文章主要介紹了Spring中@Autowired和@Resource注解相同點(diǎn)和不同點(diǎn),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01
Java instanceof關(guān)鍵字用法詳解及注意事項(xiàng)
instanceof 是 Java 的保留關(guān)鍵字。它的作用是測(cè)試它左邊的對(duì)象是否是它右邊的類(lèi)的實(shí)例,返回 boolean 的數(shù)據(jù)類(lèi)型。本文重點(diǎn)給大家介紹Java instanceof關(guān)鍵字用法詳解及注意事項(xiàng),需要的朋友參考下吧2021-09-09
MyBatisPlus唯一索引批量新增或修改的實(shí)現(xiàn)方法
本文主要介紹了MyBatisPlus唯一索引批量新增或修改的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
SpringBoot整合PageHelper實(shí)現(xiàn)分頁(yè)查詢功能詳解
PageHelper是mybatis框架的一個(gè)插件,用于支持在mybatis執(zhí)行分頁(yè)操作。本文將通過(guò)SpringBoot整合PageHelper實(shí)現(xiàn)分頁(yè)查詢功能,需要的可以參考一下2022-03-03
詳解spring boot應(yīng)用啟動(dòng)原理分析
這篇文章主要介紹了詳解spring boot應(yīng)用啟動(dòng)原理分析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06

