package com.sometest;
import java.time.Duration;
import java.time.Instant;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 缓存
**/
public class MyCache<T> {
//数据map
public Map<String, CacheObj<T>> dataMap = new ConcurrentHashMap<>();
//过期时间采集map
public Map<Long, Long> expireMap = new ConcurrentHashMap<>();
//最大限制长度
long SIZE_LIMIT;
//触发过期清理长度
long CTL;
//是否已有子线程进行清理
AtomicInteger cleaning;
//数据源
DataSrc<T> dataSrc;
//过期秒数(每个kv)
long expireSecond;
//防大规模同时过期/雪崩
int expireBalance;
//缓存创建时间
Instant createTime;
public MyCache(DataSrc dataSrc) {
this(0, dataSrc, 1800, -1);
}
public MyCache(long size, DataSrc dataSrc, long expireSecond, int expireBalance) {
this.SIZE_LIMIT = size;
this.CTL = Math.round(size * 0.6);
cleaning = new AtomicInteger(0);
this.dataSrc = dataSrc;
this.expireSecond = expireSecond;
this.expireBalance = expireBalance;
}
public T getValue(String id) {
CacheObj<T> cacheObj = dataMap.get(id);
if (cacheObj != null && !expired(cacheObj) && cacheObj.state.get() == OK_STATE) {
cacheObj.hit.addAndGet(1);
return cacheObj.value;
}
if (!limit(dataMap)) {
tryClean();
return dataSrc.getFromSrc(id);
}
CacheObj<T> obj = new CacheObj();
if (dataMap.putIfAbsent(id, obj)==null) {
obj.value = dataSrc.getFromSrc(id);
obj.ttl = expireTime(0);
obj.state.set(OK_STATE);
} else {
while (obj.state.get() == INIT_STATE) {
}
}
if (dataMap.size() > CTL) {
tryClean();
}
return obj.value;
}
private Instant expireTime(int offset) {
Instant expired = Instant.now().plusSeconds(expireSecond).plusMillis(offset);
if (expireBalance <= 0) {
return expired;
}
long l = Duration.between(createTime, expired).toMillis();
Long count = expireMap.compute(l, (k, v) -> {
if (v == null) {
return 1L;
} else if (v < expireBalance) {
return v + 1;
} else {
return v;
}
});
if (count == expireBalance) {
return expireTime(new Random().nextInt(50));
}
return expired;
}
private boolean limit(Map<String, CacheObj<T>> map) {
if (SIZE_LIMIT > 0 && map.size() >= SIZE_LIMIT) {
return true;
}
return false;
}
private void tryClean() {
if (SIZE_LIMIT <= 0) {
return;
}
if (cleaning.compareAndSet(0, 1)) {
new Thread(new Runnable() {
@Override
public void run() {
Iterator<Map.Entry<String, CacheObj<T>>> iterator = dataMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, CacheObj<T>> next = iterator.next();
if (expired(next.getValue())) {
invalid(next.getKey());
}
}
cleaning.compareAndSet(1, 0);
}
}).start();
}
}
public boolean invalid(String id) {
CacheObj cacheObj = dataMap.get(id);
if (cacheObj == null || cacheObj.state.get() != OK_STATE) {
return false;
}
return dataMap.remove(id, cacheObj);
}
public boolean expired(CacheObj<T> cacheObj) {
return cacheObj.ttl.isBefore(Instant.now());
}
public static int OK_STATE = 1;
public static int INIT_STATE = 0;
// public static int DEL_STATE = -1;
static class CacheObj<T> {
T value;
Instant ttl;
AtomicInteger state = new AtomicInteger(INIT_STATE);
AtomicInteger hit = new AtomicInteger(0);
}
interface DataSrc<T> {
T getFromSrc(String id);
}
public static void main(String[] args) {
MyCache<String> cache = new MyCache<>(new DataSrc<String>() {
@Override
public String getFromSrc(String id) {
// from DB
return id;
}
});
cache.getValue("2");
cache.getValue("2");
}
}
Please comment with your real name using good manners.