import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedTransferQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 本地缓存,到期自动删除,不管使用频率有多高,缓冲用
* @Auther yuanqiyong
*/
public class LocalCache<T> {
private Logger log = LoggerFactory.getLogger(getClass());
private Map<String, Pair<CacheKey, T>> CACHE_DATA = new ConcurrentHashMap<>();
private LinkedTransferQueue<CacheKey> QUEUE = new LinkedTransferQueue<>();
// private Long timeout = new Long(1000 * 60 * 60 * 24);// 一天
private Long timeout = new Long(1000 * 60 * 60);// 1小时
private volatile Boolean isRuning = false;
private Thread thread;
public LocalCache() {
isRuning = true;
// 开启主动删除策略;主动要比被动及时性高
AutoClear();
}
public Object get(String key, Load load) {
Object data = get(key);
if (data == null && load != null) {
data = load.load();
if (data != null) {
put(key, data);
}
}
return data;
}
public T get(String key) {
Pair<CacheKey, T> pair = CACHE_DATA.get(key);
if (pair != null) {
// 首重校验:缓存被动清理
if (pair.getKey().getExpire() <= 0 || pair.getKey().getSaveTime() > new Date().getTime()) {
return pair.getValue();
}
// 先查后删,会有线程安全问题,但对数据影响不大,无非再做次查询,而且不建议加同步块
CACHE_DATA.remove(key);
log.info("[CLAER1][" + CACHE_DATA.size() + "]:" + key);
}
return null;
}
public void put(String key, T data) {
CacheKey ck = new CacheKey(key, timeout);
Pair<CacheKey, T> pair = new Pair<CacheKey, T>(ck, data);
CACHE_DATA.put(key, pair);
QUEUE.add(ck);
log.debug("add key:" + key + "\tValue:" + pair.toString());
}
public Long getTimeout() {
return timeout;
}
public void setTimeout(Long timeout) {
this.timeout = timeout;
}
public void clear(String key) {
CACHE_DATA.remove(key);
}
public void clearAll() {
CACHE_DATA.clear();
QUEUE.clear();
}
public interface Load {
Object load();
}
private class CacheKey {
CacheKey(String key, long expire) {
this.key = key;
this.expire = expire <= 0 ? 0 : expire;
this.saveTime = new Date().getTime() + this.expire;
}
private String key;
private long saveTime; // 存活时间
private long expire; // 过期时间 小于等于0标识永久存活
public String getKey() {
return key;
}
public long getExpire() {
return expire;
}
public long getSaveTime() {
return saveTime;
}
@Override
public String toString() {
return "CacheKey [key=" + key + ", saveTime=" + saveTime + ", expire=" + expire + "]";
}
}
// 二重校验:缓存主动清理
private void AutoClear() {
thread = new Thread("LocalCache-AutoClear") {
@Override
public void run() {
while (isRuning) {
try {
CacheKey ck = QUEUE.take();// 阻塞
long now = new Date().getTime();
if (ck.getSaveTime() > now) {
long st = ck.getSaveTime() - now;
Thread.sleep(st);
}
Pair<CacheKey, T> pair = CACHE_DATA.get(ck.getKey());
if (pair != null && pair.getKey() == ck) {// CK对等才删除,防止覆盖误删
CACHE_DATA.remove(ck.getKey());
log.info("[CLAER2][" + CACHE_DATA.size() + "]:" + ck.getKey());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
thread.setDaemon(true); // 设置守护线程
thread.start();
}
public void destory() {
clearAll();
isRuning = false;
System.out.println("销毁");
}
public int size() {
return CACHE_DATA.size();
}
public static void main(String[] args) throws InterruptedException {
LocalCache<Object> lc = new LocalCache<Object>();
lc.setTimeout(1000 * 10L); //10秒钟
for (int i = 0; i < 5; i++) {
lc.put("key" + i, "data" + i);
Thread.sleep(2000);
}
// 主动策略不管这个,到点就删;被动策略通过比对删除;
// 所以同一个K,多次覆盖插入,主动策略按最后最新数据的时间算,所以存活时间=最新的设定时间;被动策略通过比对删除,存活时间固定。
lc.put("key1" , "data1");//覆盖Key
for (int i = 0; i < 20; i++) {
System.out.println("");
for (int j = 0; j < 5; j++) {
System.out.println("key" + j + ":" + lc.get("key" + j));
}
Thread.sleep(1000);
}
lc.destory();
}
}
/**
* @author Jonathan zhou
* @version 创建时间:2014-3-27 上午11:42:33
*/
public class Pair<K, V> implements Serializable {
private K key;
private V value;
public Pair(K key, V value) {
super();
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Pair<?, ?> other = (Pair<?, ?>) obj;
if (key == null) {
if (other.key != null) return false;
} else if (!key.equals(other.key)) return false;
if (value == null) {
if (other.value != null) return false;
} else if (!value.equals(other.value)) return false;
return true;
}
@Override
public String toString() {
return "Pair [key=" + key + ", value=" + value + "]";
}
}