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.