ConcurrentHashMap 实现缓存类
2024-08-25 05:48:15
参考:https://blog.csdn.net/woshilijiuyi/article/details/81335497
在规定时间内,使用 hashMap 实现一个缓存工具类,需要考虑一下几点
- 不可变对象
- 单例
- 线程安全
- 回收失效数据
- 垃圾回收
- 缓存大小
- LRU
注备:
- LRU: Least Recently Used ,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面淘汰。
- OPT : 最佳置换算法,是一种理想情况下的置换算法,但实际上不可实现。思想是标记每个页面多久后被使用,最大的将被淘汰
- FIFO:先进先出,建立一个FIFO 队列,收容所有在内存中的页,被置换的页总在队列头上进行。
- LFU : 最少使用置换算法,使用最少使用置换算法在内存中的每个页面设置一个移位寄存器,记录页面被使用的频率。
package com; import lombok.Data;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock; /**
* Created by baizhuang on 2019/11/15 10:34.
*/ public class CacheManager { private CacheManager() {
} //是否开启清除失效缓存
private volatile Boolean clearExpireCacheEnable = true; //缓存失效时间
private long cacheTimeout = 12 * 60 * 60 * 1000L; //缓存使用记录
private static LinkedList<Object> cacheUseRecord = new LinkedList<>(); //可缓存最大数量
private static Integer MAX_CACHE_SIZE = 80; //重入读写锁
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private static Lock writeLock = reentrantReadWriteLock.writeLock();
private static Lock readLock = reentrantReadWriteLock.readLock(); private final static Map<Object, CacheEntry> cacheEntryMap = new ConcurrentHashMap<>(); private void init() {
initClearTask();
} //自定义缓存失效时间
private void init(long cacheTimes) {
this.cacheTimeout = cacheTimes;
initClearTask();
} private void initClearTask() {
//启动清除失效缓存数据
if (clearExpireCacheEnable) {
new ClearCacheTask().start();
}
} private static CacheManager getCacheManagerInstance() {
return CacheManagerFactory.CACHE_MANAGER;
} private static class CacheManagerFactory {
private static final CacheManager CACHE_MANAGER = new CacheManager();
} private class ClearCacheTask extends Thread { ClearCacheTask() {
super.setName("clear cache task start ...");
} @Override
public void run() {
while (clearExpireCacheEnable) {
try {
long now = System.currentTimeMillis(); //定时清理
try {
// Thread.sleep(1000 * 60 * 60);
} catch (Exception e) {
e.printStackTrace();
} cacheEntryMap.keySet().stream().forEach(key -> {
try {
writeLock.lock(); //判断使用记录中的key是否已经被LRU清除
if (!cacheUseRecord.contains(key)) {
return;
} CacheEntry entry = cacheEntryMap.get(key);
if (now - entry.lastTouchTime >= cacheTimeout) {
cacheEntryMap.remove(key);
cacheUseRecord.remove(key);
System.out.println("清理缓存key:" + key); }
} finally {
writeLock.unlock();
}
}); Thread.sleep(cacheTimeout);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} //失效时间,value,entry,可根据需要决定是否继承Map.Entry<K,V>
@Data
private class CacheEntry {
long lastTouchTime; Object value; CacheEntry(Object value) {
super();
this.value = value;
this.lastTouchTime = System.currentTimeMillis();
}
} public Object get(Object key) { readLock.lock();
CacheEntry entry = null;
try {
entry = cacheEntryMap.get(key);
} finally {
readLock.unlock();
}
if (null == entry)
return null; //更新缓存访问时间
touchCache(entry);
//更新使用记录
touchUseRecord(key); return entry == null ? null : entry.value;
} //更新缓存访问时间
public static void touchCache(CacheEntry entry) {
writeLock.lock();
try {
entry.setLastTouchTime(System.currentTimeMillis());
} finally {
writeLock.unlock();
} } //更新缓存使用记录
public static void touchUseRecord(Object key) { writeLock.lock();
try {
//删除使用记录
cacheUseRecord.remove(key);
//新增使用记录到首位
cacheUseRecord.add(0, key);
} finally {
writeLock.unlock();
}
} public Object put(Object key, Object value) throws Exception { //判断缓存大小是否够用,否则根据LRU删除最久未使用的元素
if (cacheEntryMap.size() > MAX_CACHE_SIZE) {
deleteLRU();
}
if (cacheEntryMap.size() > MAX_CACHE_SIZE) {
throw new Exception("缓存大小超出限制");
} CacheEntry entry = new CacheEntry(value); writeLock.lock();
try {
cacheEntryMap.put(key, entry);
cacheUseRecord.add(0, key);
} finally {
writeLock.unlock();
}
return value;
} /**
* 删除最近最久未使用的缓存
*/
public static void deleteLRU() { Object cacheKey = null;
writeLock.lock();
try {
cacheKey = cacheUseRecord.remove(cacheUseRecord.size() - 1);
cacheEntryMap.remove(cacheKey);
System.out.println("LRU清除元素key:" + cacheKey);
} finally {
writeLock.unlock();
}
} public static void delete(Object key) { if (null == key)
return; writeLock.lock();
try {
cacheEntryMap.remove(key);
cacheUseRecord.remove(key);
} finally {
writeLock.unlock();
}
} public static void clear() { writeLock.lock();
try {
cacheEntryMap.clear();
cacheUseRecord.clear();
} finally {
writeLock.unlock();
}
} public static void main(String[] args) throws Exception {
CacheManager cacheManager = CacheManager.getCacheManagerInstance();
cacheManager.init(0); for (int i = 0; i < 200; i++) {
cacheManager.put(i + "", i);
}
}
}
最新文章
- 临时解决系统中大量的TIME_WAIT连接
- RocketMQ与Kafka对比(18项差异)
- JS中常遇到的浏览器兼容问题和解决方法【转】
- perl中读取外部文件
- 【Spark】---- Spark 硬件配置
- SqlServer根据时段统计数据
- (旧)子数涵数&#183;UI设计——扁平化设计
- jquery uploadify 使用
- spring 常见错误
- sql server命名规范
- 286.	Walls and Gates
- Android 安全概述
- top -bcn -1
- 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object
- php 类接口继承练习
- mock打桩之EasyMock
- .net 后台判断是否要替换
- Python scrapy爬取带验证码的列表数据
- 原生js小球运动
- 使用jquery+css实现瀑布流布局