package com.test.core.cache.impl;
import de.hybris.platform.util.Config;
import org.apache.log4j.Logger;
import java.security.SecureRandom;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MemoryCache {
private static final Logger log = Logger.getLogger(MemoryCache.class.getName());
private static ConcurrentHashMap cache = new ConcurrentHashMap<>(16);
private static SecureRandom random = new SecureRandom();
public static final long STD = random.nextInt(MemoryCacheConstant.DEFAULT_STD);
/**
* 定时移除缓存的线程
*/
private static ClearCacheThread clearCacheThread = new ClearCacheThread(cache);
static {
clearCacheThread.setName(MemoryCacheConstant.CLEAR_CACHE_THREAD_NAME);
clearCacheThread.setDaemon(true);
clearCacheThread.start();
}
private static void cache(String key, CacheBO cacheFromMemory) {
cache.put(key, cacheFromMemory);
}
public static T> T getValue(String key, RefreshCacheFunctionT> refreshCacheFunction) {
final long timeToLive = Config.getLong("memory.cache.time.stamp",MemoryCacheConstant.DEFAULT_TIME_TO_LIVE);
return getValue(key, timeToLive + STD, refreshCacheFunction);
}
public static T> T getValue(String key, long timeToLive, RefreshCacheFunctionT> refreshCacheFunction) {
T value = null;
CacheBOT> cacheFromMemory = MemoryCache.getValue(key);
boolean valid = false;
long now = System.currentTimeMillis();
if (cacheFromMemory != null) {
//防缓存雪崩,timeToLive会包含随机误差
if (cacheFromMemory.getTimestamp() != null && now - cacheFromMemory.getTimestamp() < timeToLive) {
valid = true;
}
}
if (!valid) {
try {
//intern确保字符串直接从常量池获取,避免stringBuilder等new操作返回不同的string对象
synchronized (key.intern()) {
CacheBOT> cacheFromMemoryNew = MemoryCache.getValue(key);
if (cacheFromMemoryNew == null || cacheFromMemoryNew.getTimestamp() == null || cacheFromMemoryNew.getTimestamp() < now) {
value = refreshCacheFunction.refresh();
//添加二级缓存
cacheFromMemoryNew = new CacheBO<>();
cacheFromMemoryNew.setValue(value);
cacheFromMemoryNew.setTimestamp(System.currentTimeMillis());
MemoryCache.cache(key, cacheFromMemoryNew);
}
value = cacheFromMemoryNew.getValue();
}
} catch (Exception e) {
log.error("刷新二级缓存失败", e);
}
} else {
value = cacheFromMemory.getValue();
}
return value;
}
private static T> T getValue(String key) {
return (T) cache.get(key);
}
public static void pushCache(String key,Map value){
CacheBO> cacheFromMemoryNew = new CacheBO<>();
cacheFromMemoryNew.setValue(value);
cacheFromMemoryNew.setTimestamp(System.currentTimeMillis());
MemoryCache.cache(key, cacheFromMemoryNew);
}
}
/**
* @author Zhi.Dong
* 定时清除二级缓存的无效数据
*/
class ClearCacheThread extends Thread {
private ConcurrentHashMap cache;
private static final Logger log = Logger.getLogger(ClearCacheThread.class.getName());
ClearCacheThread(ConcurrentHashMap cache) {
this.cache = cache;
}
@Override
public void run() {
try {
while (true) {
try {
clearCache();
Thread.sleep(MemoryCacheConstant.DEFAULT_TIME_TO_EXE_CLEAR);
} catch (Exception e) {
log.error("ClearCacheThread inLoop 清除缓存失败", e);
}
}
} catch (Exception e) {
log.error("ClearCacheThread 清除缓存失败", e);
}
}
/**
* 清除缓存里过期的数据
*/
private void clearCache() {
long now = System.currentTimeMillis();
for (Map.Entry entry : cache.entrySet()) {
CacheBO v = entry.getValue();
boolean clear = true;
//数据有效则不删除,此有效时间不同于缓存查询的有效时间
//仅用于清除过期时间已过很久的数据
if (v != null) {
if (v.getTimestamp() != null && now - v.getTimestamp() < MemoryCacheConstant.DEFAULT_TIME_TO_CLEAR) {
clear = false;
}
}
if (clear) {
log.info(String.format("clearCache success,key=%s", entry.getKey()));
cache.remove(entry.getKey());
}
}
}
}
MemoryCacheConstant
package com.test.core.cache.impl;
/**
*
* 二级缓存相关常量
*/
public interface MemoryCacheConstant {
/**
* 默认缓存有效期,10s
*/
Long DEFAULT_TIME_TO_LIVE = 10000L;
/**
* 默认防雪崩随机误差,5s
*/
Integer DEFAULT_STD = 5000;
/**
* 定时线程名称
*/
String CLEAR_CACHE_THREAD_NAME = "clear-memory-cache-thread";
/**
* 默认缓存清除时间,5分钟
*/
Long DEFAULT_TIME_TO_CLEAR = 5 * 60 * 1000L;
/**
* 默认缓存清除触发间隔,60s
*/
Long DEFAULT_TIME_TO_EXE_CLEAR = 60 * 1000L;
}
RefreshCacheFunction
package com.test.core.cache.impl;
public interface RefreshCacheFunctionT> {
/**
* 返回用于刷新二级缓存的结果
* @return
*/
T refresh();
}