参考:https://blog.csdn.net/woshilijiuyi/article/details/81335497

在规定时间内,使用 hashMap 实现一个缓存工具类,需要考虑一下几点

  1. 不可变对象
  2. 单例
  3. 线程安全
  4. 回收失效数据
  5. 垃圾回收
  6. 缓存大小
  7. 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 实现缓存类的更多相关文章

  1. ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core

    背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...

  2. 分享个 之前写好的 android 文件流缓存类,专门处理 ArrayList、bean。

    转载麻烦声明出处:http://www.cnblogs.com/linguanh/ 目录: 1,前序 2,作用 3,特点 4,代码 1,前序  在开发过程中,client 和 server 数据交流一 ...

  3. (实用篇)PHP缓存类完整实例

    本文完整描述了一个简洁实用的PHP缓存类,可用来检查缓存文件是否在设置更新时间之内.清除缓存文件.根据当前动态文件生成缓存文件名.连续创建目录.缓存文件输出静态等功能.对于采用PHP开发CMS系统来说 ...

  4. php简单缓存类

    <?phpclass Cache {    private $cache_path;//path for the cache    private $cache_expire;//seconds ...

  5. ASP缓存类收集

    木鸟写的 '********************************************** ' vbs Cache类 ' ' 属性valid,是否可用,取值前判断 ' 属性name,ca ...

  6. php简单数据缓存类

    公司手机触屏站 ,由于页面图片太多,所以需要做数据缓存,就随便写一个数据缓存类. 直接贴代码 <?php/**** fianl_m@foxmail.com* 缓存类* 把数据查询出,并序列化写入 ...

  7. iOS缓存类的设计

    使用执行速度缓存的程序可以大大提高程序,设计一个简单的缓存类并不需要太复杂的逻辑. 只需要一个简单的3接口. 存款对象 以一个对象 删除对象 阅读对象 watermark/2/text/aHR0cDo ...

  8. 一个不错的PHP文件页面缓存类

    在php中缓存分类数据库缓存,文件缓存和内存缓存,下面我来给各位同学详细介绍PHP文件缓存类实现代码,有需要了解的朋友可参考. 页面缓存类 <?php    /*    * 缓存类    cac ...

  9. Java缓存类的实际应用场景

    不要着迷于技术,应把注意力放到问题上. 一个普通的后台管理系统,一定会有参数配置.参数配置数据表和其他的数据表是不同的,它的操作基本都是查的操作.参数配置的这些数据信息是贯穿在整个项目中,那么把他们放 ...

随机推荐

  1. webpack配置的说明

    {devtool: 'source-map',//要启用source-map需加上此配置项,同时css或less的loader要加上参数?sourceMap,js的loader不用加 entry: e ...

  2. java.sql.SQLException: Field 'login_date' doesn't have a default value解决方法

    在做web项目的insert插入操作的时候, 由于对于一个字段没有插入数据, xml文件写法如下: <insert id="savePremissUser" > ins ...

  3. JS字符串的不可变性

    js中的字符串特性->不可变性,字符串的值是不可变的 1.改变字符串中的字符 var str = "hello"; str[1] = "W"; conso ...

  4. 【做题笔记】P2871 [USACO07DEC]手链Charm Bracelet

    就是 01 背包.大意:给您 \(T\) 个空间大小的限制,有 \(M\) 个物品,第 \(i\) 件物品的重量为 \(c_i\) ,价值为 \(w_i\) .要求挑选一些物品,使得总空间不超过 \( ...

  5. CodeForces Gym 100213F Counterfeit Money

    CodeForces Gym题目页面传送门 有\(1\)个\(n1\times m1\)的字符矩阵\(a\)和\(1\)个\(n2\times m2\)的字符矩阵\(b\),求\(a,b\)的最大公共 ...

  6. MySQL执行外部sql脚本文件的命令

    sql脚本是包含一到多个sql命令的sql语句,我们可以将这些sql脚本放在一个文本文件中(我们称之为“sql脚本文件”),然后通过相关的命令执行这个sql脚本文件.基本步骤如下:1.创建包含sql命 ...

  7. CDH安装时,无法纳管全部的节点的一个bug

      问题描述: 使用CDH 5.2版本安装时,agent节点有12个.按照安装说明,在各个节点启动cm-agent之后,发现只有6个节点能被纳管.其它的节点总是无法加入纳管中. 在确认防火墙已经关闭后 ...

  8. c/c++学习01

    c++指针初始赋值: //指针初始赋值 int* a = new int(3); //第二种赋值 int 初始值 = 100; int *b = &初始值; //由new分配的内存块通常使用过 ...

  9. LUA利用第三方API访问数据库

    ===========数据库访问--第三方 http { upstream backend { drizzle_server 192.168.4.119:3306 protocol=mysql dbn ...

  10. mybatis--多对多关联

    mybatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作.因此在进行实体类多对多映射表 ...