ConcurrentHashMap 实现缓存类
参考: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);
}
}
}
ConcurrentHashMap 实现缓存类的更多相关文章
- ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core
背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...
- 分享个 之前写好的 android 文件流缓存类,专门处理 ArrayList、bean。
转载麻烦声明出处:http://www.cnblogs.com/linguanh/ 目录: 1,前序 2,作用 3,特点 4,代码 1,前序 在开发过程中,client 和 server 数据交流一 ...
- (实用篇)PHP缓存类完整实例
本文完整描述了一个简洁实用的PHP缓存类,可用来检查缓存文件是否在设置更新时间之内.清除缓存文件.根据当前动态文件生成缓存文件名.连续创建目录.缓存文件输出静态等功能.对于采用PHP开发CMS系统来说 ...
- php简单缓存类
<?phpclass Cache { private $cache_path;//path for the cache private $cache_expire;//seconds ...
- ASP缓存类收集
木鸟写的 '********************************************** ' vbs Cache类 ' ' 属性valid,是否可用,取值前判断 ' 属性name,ca ...
- php简单数据缓存类
公司手机触屏站 ,由于页面图片太多,所以需要做数据缓存,就随便写一个数据缓存类. 直接贴代码 <?php/**** fianl_m@foxmail.com* 缓存类* 把数据查询出,并序列化写入 ...
- iOS缓存类的设计
使用执行速度缓存的程序可以大大提高程序,设计一个简单的缓存类并不需要太复杂的逻辑. 只需要一个简单的3接口. 存款对象 以一个对象 删除对象 阅读对象 watermark/2/text/aHR0cDo ...
- 一个不错的PHP文件页面缓存类
在php中缓存分类数据库缓存,文件缓存和内存缓存,下面我来给各位同学详细介绍PHP文件缓存类实现代码,有需要了解的朋友可参考. 页面缓存类 <?php /* * 缓存类 cac ...
- Java缓存类的实际应用场景
不要着迷于技术,应把注意力放到问题上. 一个普通的后台管理系统,一定会有参数配置.参数配置数据表和其他的数据表是不同的,它的操作基本都是查的操作.参数配置的这些数据信息是贯穿在整个项目中,那么把他们放 ...
随机推荐
- webpack配置的说明
{devtool: 'source-map',//要启用source-map需加上此配置项,同时css或less的loader要加上参数?sourceMap,js的loader不用加 entry: e ...
- java.sql.SQLException: Field 'login_date' doesn't have a default value解决方法
在做web项目的insert插入操作的时候, 由于对于一个字段没有插入数据, xml文件写法如下: <insert id="savePremissUser" > ins ...
- JS字符串的不可变性
js中的字符串特性->不可变性,字符串的值是不可变的 1.改变字符串中的字符 var str = "hello"; str[1] = "W"; conso ...
- 【做题笔记】P2871 [USACO07DEC]手链Charm Bracelet
就是 01 背包.大意:给您 \(T\) 个空间大小的限制,有 \(M\) 个物品,第 \(i\) 件物品的重量为 \(c_i\) ,价值为 \(w_i\) .要求挑选一些物品,使得总空间不超过 \( ...
- CodeForces Gym 100213F Counterfeit Money
CodeForces Gym题目页面传送门 有\(1\)个\(n1\times m1\)的字符矩阵\(a\)和\(1\)个\(n2\times m2\)的字符矩阵\(b\),求\(a,b\)的最大公共 ...
- MySQL执行外部sql脚本文件的命令
sql脚本是包含一到多个sql命令的sql语句,我们可以将这些sql脚本放在一个文本文件中(我们称之为“sql脚本文件”),然后通过相关的命令执行这个sql脚本文件.基本步骤如下:1.创建包含sql命 ...
- CDH安装时,无法纳管全部的节点的一个bug
问题描述: 使用CDH 5.2版本安装时,agent节点有12个.按照安装说明,在各个节点启动cm-agent之后,发现只有6个节点能被纳管.其它的节点总是无法加入纳管中. 在确认防火墙已经关闭后 ...
- c/c++学习01
c++指针初始赋值: //指针初始赋值 int* a = new int(3); //第二种赋值 int 初始值 = 100; int *b = &初始值; //由new分配的内存块通常使用过 ...
- LUA利用第三方API访问数据库
===========数据库访问--第三方 http { upstream backend { drizzle_server 192.168.4.119:3306 protocol=mysql dbn ...
- mybatis--多对多关联
mybatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作.因此在进行实体类多对多映射表 ...