参考: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. Equalize

    You are given two binary strings aa and bb of the same length. You can perform the following two ope ...

  2. Go网络编程TCP

    1. 服务端 package main import ( "bufio" "fmt" "net" "os" " ...

  3. DuPan不限速教程

    准备: 1.一个百度网盘链接 2.一个可以切换UA的浏览器, 手机版:via,极速浏览器,Kiwi浏览器(推荐)电脑版:未知 3.你的手和脑子

  4. winform学习(6)控件的对齐、比例、定位操作

    窗体控件的常用操作 选中一个控件,按住Ctrl再点选另一个控件(此时编辑的是第二个控件,当使用框选选中多个控件时,编辑的是黑色锚点的控件) 代码中的居顶和居底: 控件标识符.BringToFront( ...

  5. Spring Boot中快速操作Mongodb

    Spring Boot中快速操作Mongodb 在Spring Boot中集成Mongodb非常简单,只需要加入Mongodb的Starter包即可,代码如下: <dependency> ...

  6. Codeforces C. Almost Equal (数学规律)

    题目链接:http://codeforces.com/contest/1206/problem/C 题解 : 观察可以发现当n为偶数时,1 - 2n是不满足题意的,可以举例n = 2,n = 4试一试 ...

  7. IntelliJ IDEA 2017.3尚硅谷-----取消标题单行显示

  8. c语言thread用法记录。

    https://blog.csdn.net/hitwengqi/article/details/8015646 先是c++11之前的 1.最基础,进程同时创建5个线程,各自调用同一个函数 #inclu ...

  9. buuctf

    大白 | png图片改高度png图片改高度[外链图片转存失败(img-PojN2D3v-1567086301372)(evernotecid://74A3E6DA-E009-4797-AA60-5DE ...

  10. HTML5圆形百分比进度条插件circleChart

    在页面中引入jquery和circleChart.min.js文件. <script src="path/to/jquery.min.js"></script&g ...