源碼地址: GitHub
使用場景
在Java
應(yīng)用中橘霎,對于訪問頻率高蔫浆,更新少的數(shù)據(jù),通常的方案是將這類數(shù)據(jù)加入緩存中姐叁。相對從數(shù)據(jù)庫中讀取來說瓦盛,讀緩存效率會(huì)有很大提升。
在集群環(huán)境下外潜,常用的分布式緩存有Redis
原环、Memcached
等。但在某些業(yè)務(wù)場景上处窥,可能不需要去搭建一套復(fù)雜的分布式緩存系統(tǒng)嘱吗,在單機(jī)環(huán)境下,通常是會(huì)希望使用內(nèi)部的緩存(LocalCache
)滔驾。
實(shí)現(xiàn)
這里提供了兩種LocalCache
的實(shí)現(xiàn)谒麦,一種是基于ConcurrentHashMap
實(shí)現(xiàn)基本本地緩存,另外一種是基于LinkedHashMap
實(shí)現(xiàn)LRU
策略的本地緩存嵌灰。
基于ConcurrentHashMap的實(shí)現(xiàn)
static {
timer = new Timer();
map = new ConcurrentHashMap<>();
}
以ConcurrentHashMap
作為緩存的存儲(chǔ)結(jié)構(gòu)弄匕。因?yàn)?code>ConcurrentHashMap的線程安全的,所以基于此實(shí)現(xiàn)的LocalCache
在多線程并發(fā)環(huán)境的操作是安全的沽瞭。在JDK1.8
中迁匠,ConcurrentHashMap
是支持完全并發(fā)讀,這對本地緩存的效率也是一種提升驹溃。通過調(diào)用ConcurrentHashMap
對map
的操作來實(shí)現(xiàn)對緩存的操作城丧。
私有構(gòu)造函數(shù)
private LocalCache() {
}
LocalCache
是工具類,通過私有構(gòu)造函數(shù)強(qiáng)化不可實(shí)例化的能力豌鹤。
緩存清除機(jī)制
/**
* 清除緩存任務(wù)類
*/
static class CleanWorkerTask extends TimerTask {
private String key;
public CleanWorkerTask(String key) {
this.key = key;
}
public void run() {
LocalCache.remove(key);
}
}
清理失效緩存是由Timer
類實(shí)現(xiàn)的亡哄。內(nèi)部類CleanWorkerTask
繼承于TimerTask
用戶清除緩存。每當(dāng)新增一個(gè)元素的時(shí)候布疙,都會(huì)調(diào)用timer.schedule
加載清除緩存的任務(wù)蚊惯。
基于LinkedHashMap的實(shí)現(xiàn)
以LinkedHashMap
作為緩存的存儲(chǔ)結(jié)構(gòu)愿卸。主要是通過LinkedHashMap
的按照訪問順序的特性來實(shí)現(xiàn)LRU
策略。
LRU
LRU
是Least Recently Used
的縮寫截型,即最近最久未使用趴荸。LRU
緩存將會(huì)利用這個(gè)算法來淘汰緩存中老的數(shù)據(jù)元素,從而優(yōu)化內(nèi)存空間宦焦。
基于LRU策略的map
這里利用LinkedHashMap
來實(shí)現(xiàn)基于LRU
策略的map
发钝。通過調(diào)用父類LinkedHashMap
的構(gòu)造函數(shù)來實(shí)例化map
。參數(shù)accessOrder
設(shè)置為true
保證其可以實(shí)現(xiàn)LRU
策略波闹。
static class LRUMap<K, V> extends LinkedHashMap<K, V> {
... // 省略部分代碼
public LRUMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
... // 省略部分代碼
/**
* 重寫LinkedHashMap中removeEldestEntry方法;
* 新增元素的時(shí)候,會(huì)判斷當(dāng)前map大小是否超過DEFAULT_MAX_CAPACITY,超過則移除map中最老的節(jié)點(diǎn);
*
* @param eldest
* @return
*/
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > DEFAULT_MAX_CAPACITY;
}
}
線程安全
/**
* 讀寫鎖
*/
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock rLock = readWriteLock.readLock();
private final Lock wLock = readWriteLock.writeLock();
LinkedHashMap
并不是線程安全酝豪,如果不加控制的在多線程環(huán)境下使用的話,會(huì)有問題精堕。所以在LRUMap
中引入了ReentrantReadWriteLock
讀寫鎖孵淘,來控制并發(fā)問題。
緩存淘汰機(jī)制
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > DEFAULT_MAX_CAPACITY;
}
此處重寫LinkedHashMap
中removeEldestEntry
方法锄码, 當(dāng)緩存新增元素的時(shí)候,會(huì)判斷當(dāng)前map
大小是否超過DEFAULT_MAX_CAPACITY
,超過則移除map中最老的節(jié)點(diǎn)夺英。
緩存清除機(jī)制
緩存清除機(jī)制與ConcurrentHashMap
的實(shí)現(xiàn)一致,均是通過timer
實(shí)現(xiàn)滋捶。