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缓存类的实际应用场景
不要着迷于技术,应把注意力放到问题上. 一个普通的后台管理系统,一定会有参数配置.参数配置数据表和其他的数据表是不同的,它的操作基本都是查的操作.参数配置的这些数据信息是贯穿在整个项目中,那么把他们放 ...
随机推荐
- 转载:声音分贝的概念,dBSPL.dBFS
转自:https://www.cnblogs.com/wangguchangqing/p/5947659.html 分贝 Decibel 分贝(dB)是一个对数单位(logarithmic unit) ...
- win7的mstsc无法远程的解决方法
第一,开启远程功能,计算机上右击-属性,如下图. 第二,打开服务,开启这个,如下图 第三,为要远程的计算机设置账号和密码, 第四,mstsc时,可以设置记住密码,这样简单.
- 左偏树(p1456) 比较模板的一道题
题意:有n只猴子,m个操作,每一个操作,会让这两堆猴子里的最大的两只打架,打完之后,自身权值减半,然后他们会成为朋友 也就是会属于同一棵树,细节:如果选出的猴子在同一堆,就输出-1,然后下一个操作,不 ...
- Python - Git for windows 下使用 交互式命令行
1. 概述 尝试在 windows 环境的 git 命令行使用 python3 的交互模式 2. 环境 OS win10 1905 git 2.20.1 mintty 2.9.4 python 3.7 ...
- Docker - CentOS 7 安装
1. 概述 安装 docker markdown 显示有点问题 代码块里的 后面应该跟一个换行, 但是没有跟 这样会导致部分命令直接执行没有反应 2. 环境 os CentOS7 用户 root 3 ...
- Unity切换场景后变暗
这个问题估计很多人都碰到过,原因是切换场景的光照贴图数据丢失,解决方案如下: 打开你要切换的场景,打开Windows-Lighting-Settings,将最下面的Auto Generate前面的勾去 ...
- 粪发涂墙-java1
相信很多人和笔者一样,经常会做一些数组的初始化工作,也肯定会经常用到集合类.假如我现在要初始化一个String类型的数组,可以很方便的使用如下代码: String [] strs = {"T ...
- 在Linux系统下安装nginx教程
最近学习了nginx,就打算nginx安装在Linux系统下,于是我就把安装步骤记录下来了,分享给大家,希望能对大家有帮助! 我的博客地址:https://www.cnblogs.com/themys ...
- STM32F103之I2C学习记录
26.3.1 模式选择 该外设可以在以下四种模式之一 1)从机发送模式 2)从机接收模式 3)主机发送模式 4)主机接收模式 IIC协议时序 MSB:Most Significant Bit(最高有 ...
- Django ORM中的模糊查询
ORM映射 什么是ORM映射?在笔者认为就是对SQL语句的封装,所写语句与SQL对应语句含义相同,使开发更加简单方便,不过也是存在弊端的,使程序运行效率下降.例如: UserInfo.objects. ...