资讯专栏INFORMATION COLUMN

本地缓存工具类

RichardXG / 654人阅读

摘要:时间转换成毫秒清空当前命名空间下的所有暴露指定命名空间下所有的缓存死亡时间纳秒值对更新缓存时旧的已有的会取消重新设置新的对于每个是单例的

package com.common.helper;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.lang.Nullable;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 本地缓存管理器
 * 
 *     v1.1
 *       - getByNameSpace
 *       - isExpired(..) 判断是否已经死亡, 包含物理死亡和逻辑死亡.
 *
 *     v1.0
 *       - 增加构造, 可指定线程数
 *
 *     v0.0.1
 *       - 测试期
 * 
* * @author Nisus Liu * @version 1.1 * @email liuhejun108@163.com * @date 2018/11/7 17:55 */ @Slf4j public abstract class LocalCacheManager { public static final String NAMESPACE_KEY_JOIN = "::"; /** * 缓存池子 */ protected Map caches = new ConcurrentHashMap(); /** * 清除缓存的定时延迟任务(刽子手) */ protected Map headsmans = new ConcurrentHashMap(); protected ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(5); public LocalCacheManager() { } /** * @param poolSize 指定用于清除定时任务的线程数 */ public LocalCacheManager(int poolSize) { scheduler.setCorePoolSize(poolSize); } /** * 获取指定key的缓存值 * * @param key * @return */ public T get(@Nullable String nameSpace, String key) { if (StringUtil.isEmpty(key)) { return null; } if (!StringUtil.isEmpty(nameSpace)) { //key实际上是原生key加上命名空间前缀 key = joinNameSpaceAndKey(nameSpace, key); } if (isExpired(key)) return null; return caches.get(key); } /** * 判断是否死亡(实际死亡/逻辑死亡) *

逻辑死亡: 时间上看已经过期, 但是由于秦楚线程阻塞, 还没有来得及清除

* * @param nskey joinNameSpaceAndKey * @return */ private boolean isExpired(String nskey) { //防止延迟清除任务阻塞, 导致生存时间误差, 这里实现逻辑清除 Headsman headsman = headsmans.get(nskey); if (headsman != null) { //死亡时间 <= 当前时间, 应该设置为逻辑死亡, 返回null if (headsman.triggerTime <= System.nanoTime()) { // ?已经逻辑死亡, 但实际没有死亡的值是否要在这里清除呢? return true; } else { return false; } } // else: 物理死亡 return !caches.containsKey(nskey); } private String joinNameSpaceAndKey(String nameSpace, String key) { if (StringUtils.isBlank(key)) { log.debug("`key` is NULL, return"); return null; } if (!StringUtil.isEmpty(nameSpace)) { return nameSpace + NAMESPACE_KEY_JOIN + key; } return key; } /** * 增加OR更新缓存 * * @param key * @param value */ public boolean put(@Nullable String nameSpace, @NotNull String key, T value) { key = joinNameSpaceAndKey(nameSpace, key); if (key == null) { return false; } caches.put(key, value); return true; } /** * 更加OR更新缓存(可设置生存时间TTL,Time To Live) *
     *     Note: 注意避免并发对同一个 key 设置TTL, 结果比较随机, 谁最后执行, 就以谁的TTL设置为准.
     * 
* * @param key * @param value * @param ttl * @param unit */ @Deprecated public void put(@Nullable String nameSpace, String key, T value, long ttl, TimeUnit unit) { String nsKey = joinNameSpaceAndKey(nameSpace, key); put(null, nsKey, value); long triggerTime = System.nanoTime() + unit.toNanos(ttl); //若此 key 已经有对应的杀死任务, 需要替换掉, 更新生存时间, 以最新的为准 Headsman headsman = headsmans.get(nsKey); if (headsman == null) { //增加缓存 headsman = new Headsman(); headsmans.put(nsKey, headsman); headsman.task = new Runnable() { @Override public void run() { //指定时间后清除此 nsKey T rm = caches.remove(nsKey); log.trace("cache expired, key: {}, value: {}", nsKey, rm); } }; } else { //更新缓存 //对于已经有设置过缓存的 nsKey, 任务用已有的, scheduledFuture 先取消旧的, 在new新的 /*如果任务运行之前调用了该方法,那么任务就不会被运行; 如果任务已经完成或者已经被取消,那么该方法方法不起作用; 如果任务正在运行,并且 cancel 传入参数为 true,那么便会去终止与 Future 关联的任务。*/ headsman.scheduledFuture.cancel(true); } headsman.scheduledFuture = new FutureTask(headsman.task, null); //时间转换成毫秒 headsman.triggerTime = triggerTime; scheduler.schedule(headsman.scheduledFuture, ttl, unit); } public void put(@Nullable String nameSpace, String key, T value, Duration ttl) { String nsKey = joinNameSpaceAndKey(nameSpace, key); put(null, nsKey, value); long triggerTime = System.nanoTime() + ttl.toNanos(); //若此 key 已经有对应的杀死任务, 需要替换掉, 更新生存时间, 以最新的为准 Headsman headsman = headsmans.get(nsKey); if (headsman == null) { //增加缓存 headsman = new Headsman(); headsmans.put(nsKey, headsman); headsman.task = new Runnable() { @Override public void run() { //指定时间后清除此 nsKey T rm = caches.remove(nsKey); log.trace("cache expired, key: {}, value: {}", nsKey, rm); } }; } else { //更新缓存 //对于已经有设置过缓存的 nsKey, 任务用已有的, scheduledFuture 先取消旧的, 在new新的 /*如果任务运行之前调用了该方法,那么任务就不会被运行; 如果任务已经完成或者已经被取消,那么该方法方法不起作用; 如果任务正在运行,并且 cancel 传入参数为 true,那么便会去终止与 Future 关联的任务。*/ headsman.scheduledFuture.cancel(true); } headsman.scheduledFuture = new FutureTask(headsman.task, null); //时间转换成毫秒 headsman.triggerTime = triggerTime; scheduler.schedule(headsman.scheduledFuture, ttl.toMillis(), TimeUnit.MILLISECONDS); } public T evictCache(String nameSpace, String key) { if (key == null) { return null; } String nsKey = joinNameSpaceAndKey(nameSpace, key); Headsman hsm = headsmans.remove(nsKey); if (hsm != null) hsm.scheduledFuture.cancel(true); return caches.remove(nsKey); } /** * 清空当前命名空间下的所有value */ public void evictCache(String nameSpace) { String prefix = nameSpace + NAMESPACE_KEY_JOIN; caches.forEach((k, v) -> { if (k.startsWith(prefix)) { evictCache(null, k); } }); } public void evictAllCache() { caches.clear(); } /** * 暴露指定命名空间下所有的缓存 * * @param nameSpace * @return */ public List getByNameSpace(String nameSpace) { String prefix = nameSpace + NAMESPACE_KEY_JOIN; List nsVals = new ArrayList<>(); caches.forEach((k, v) -> { if (k.startsWith(prefix)) { if (!isExpired(k)) { nsVals.add(v); } } }); return nsVals; } class Headsman { /** * 死亡时间, 纳秒值 */ public long triggerTime; /** * 对key更新缓存时, 旧的已有的会取消, 重新设置新的. */ public FutureTask scheduledFuture; /** * 对于每个 key, task是单例的 */ public Runnable task; } }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/62131.html

相关文章

  • Android 图片三级缓存机制工具封装

    摘要:注意需要静态初始化软引用,可保证全局有效。实现的方法是通过集成一个,并定制里面的内容。 Android 图片三级缓存机制工具类封装 三级缓存分别是: 软引用SoftReference,cache在内存中 文件缓存在本地SDcard文件夹中,遇到文件名相同的图片则从本地取,且加入软引用中 从网络下载,并保存在本地,且加入软引用中 其中缓存的文件,其文件名经过MD5转换,去掉了文件...

    Ajian 评论0 收藏0
  • 本地缓存工具

    摘要:时间转换成毫秒清空当前命名空间下的所有暴露指定命名空间下所有的缓存死亡时间纳秒值对更新缓存时旧的已有的会取消重新设置新的对于每个是单例的 package com.common.helper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.jetbrai...

    邹立鹏 评论0 收藏0
  • NDK开发-JNI 局部引用、全局引用和弱全局引用

    摘要:三种引用简介及区别在规范中定义了三种引用局部引用全局引用弱全局引用。你可能会为了提高程序的性能,在函数中将局部引用存储在静态变量中缓存起来,供下次调用时使用。 看到一篇写的很不错的介绍JNI各种引用的文章 mark 了。   这篇文章比较偏理论,详细介绍了在编写本地代码时三种引用的使用场景和注意事项。可能看起来有点枯燥,但引用是在 JNI 中最容易出错的一个点,如果使用不当,容易使程序...

    xiangchaobin 评论0 收藏0
  • Fortune-Commons正式开源啦

    摘要:是笔者在工作以来的一些技术积累,虽然是很低端,但是对于入门或者初入开发的工作者来说,也是一个不错的学习资源,今天特地整合出来。现在的状态是成功创建的新的分支并且已经切换到新分支上。 Fortune Commons 是笔者在工作以来的一些技术积累,虽然是很[低端],但是对于Java入门或者初入Java开发的工作者来说,也是一个不错的学习资源,今天特地整合出来。 Github地址:http...

    Mr_zhang 评论0 收藏0
  • Fortune-Commons正式开源啦

    摘要:是笔者在工作以来的一些技术积累,虽然是很低端,但是对于入门或者初入开发的工作者来说,也是一个不错的学习资源,今天特地整合出来。现在的状态是成功创建的新的分支并且已经切换到新分支上。 Fortune Commons 是笔者在工作以来的一些技术积累,虽然是很[低端],但是对于Java入门或者初入Java开发的工作者来说,也是一个不错的学习资源,今天特地整合出来。 Github地址:http...

    Joonas 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<