⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_37176126/article/details/82837452 「E1ffC1」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

很多场景下,有些请求的数据,是不会经常改变的,这种时候,为了减少数据库的查询压力,可以将这一部分数据放入缓存中,直接从缓存中读取。除了一些像Redis等缓存外,还可以通过本地内存,作为缓存。下边将使用ConcurrentHashMap来实现本地缓存。

相关的技术:

  • ConcurrentHashMap --数据存储,线程安全的map
  • ScheduledExecutorService --线程定时调度服务
  • TimerTask --定时任务
  • lambda表达式

整体思路

  1. 用线程安全的ConcurrentHashMap来作为缓存数据的存储,
  2. 然后通过定时调度任务TimerTask,来实现控制缓存的有效时间,根据缓存设置的超时时间,来定时清除对应的 key,实现缓存过期
  3. 实现一些静态方法,来增加缓存、获取缓存等

定时任务也可也以用Timer来进行调度,但是Timer与ScheduledExecutorService相比有一些缺陷,具体对比,可以另行查看。

  1. 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题
  2. Timer内部是一个线程,任务1所需的时间超过了两个任务间的间隔时会导致问题
  3. Timer执行周期任务时依赖系统时间

LocalCache整体结构

初始化数据

/**
* 默认缓存时长 单位s
*/
private static final int DEFAULT_TIMEOUT = 3600;
/**
* 默认缓存容量
*/
private static final int DEFAULT_SIZE = 1000;

/**
* 存储数据
*/
private static final Map<String,Object> data;

private static final ScheduledExecutorService executorService;

//初始化
static {
data = new ConcurrentHashMap<>(DEFAULT_SIZE);
executorService = new ScheduledThreadPoolExecutor(2);
}
/**
* 私有化构造函数
*/
private LocalCache(){}

删除缓存的定时任务

定时任务主要是实现TimerTask类中的run方法,传入对应的key,然后从缓存中移除对应的键值对,所以实现方式有三种:静态内部类、匿名内部类、以及lambda方式,选择熟悉的一种即可

//静态内部类
static class CacheCleanTask extends TimerTask {

private String key;

private CacheCleanTask(String key){
this.key = key;
}

public static CacheCleanTask cacheTask(String key){
return new CacheCleanTask(key);
}

@Override
public void run() {
//移除对应 key
LocalCache.remove(key);
}
}

增加缓存

/**
* 增加缓存 默认有效时长
* @param key
* @param value
*/
public static void put(String key, Object value){
data.put(key,value);
//定时器 调度任务,用于根据 时间 定时清除 对应key 缓存
executorService.schedule(new TimerTask() {
@Override
public void run() {
remove(key);
}
}, DEFAULT_TIMEOUT, TimeUnit.SECONDS);
}

/**
* 增加缓存 并设置缓存时长 单位 s
* @param key
* @param value
* @param timeout 缓存时长 单位s
*/
public static void put(String key, Object value, int timeout){
data.put(key, value);
//lambda 替换匿名内部类
executorService.schedule(() -> remove(key), timeout, TimeUnit.SECONDS);
}

获取缓存

/**
* 获取缓存
* @param key
* @return
*/
public static Object get(String key){
return data.get(key);
}

/**
* 获取当前缓存中 所有的key
* @return
*/
public static Set<String> cacheKeys(){
return data.keySet();
}

删除缓存

/**
* 删除缓存
* @param key
*/
public static void remove(String key){
data.remove(key);
}

/**
* 清空所有缓存
*/
public static void clear(){
if(size() > 0){
data.clear();
}
}

测试方法就不贴了,其他更多相关方法的实现,可以看一下GitHub上源码的具体实现。

https://github.com/liuyuanju7/Web-Crawler/tree/master/wangyiyun/src/main/java/com/liuyj/core/cache

文章目录
  1. 1. 整体思路
  2. 2. LocalCache整体结构
  3. 3. 初始化数据
  4. 4. 删除缓存的定时任务
  5. 5. 增加缓存
  6. 6. 获取缓存
  7. 7. 删除缓存