摘要:時間轉換成毫秒清空當前命名空間下的所有暴露指定命名空間下所有的緩存死亡時間納秒值對更新緩存時舊的已有的會取消重新設置新的對于每個是單例的
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 ListgetByNameSpace(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; } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75703.html
閱讀 3197·2021-11-08 13:18
閱讀 1353·2021-10-09 09:57
閱讀 1182·2021-09-22 15:33
閱讀 3960·2021-08-17 10:12
閱讀 5053·2021-08-16 11:02
閱讀 2676·2019-08-30 10:56
閱讀 962·2019-08-29 18:31
閱讀 3251·2019-08-29 16:30