緩存實現思路
阿新 • • 發佈:2018-08-12
線程不安全 時間計算 rri 實現思路 復雜 all 安全 syn 其他
緩存主要為了解決各個組件之間讀取速度不匹配問題,比如寄存器是L1的緩存,L1是L2的緩存,L2是L3的緩存,L3是內存的緩存等。通過讀Java Concurrency Practice P85,實現了一個簡單可以添加和獲取數據的緩存。其它的諸如緩存過期,更新緩存等沒有實現- -!!
代碼
計算接口,用到了裝飾者模式。
public interface Computable<A,V> {
V compute(A arg);
}
一種計算的實現
public class ExpensiveFunction implements Computable<String,BigInteger> { @Override public BigInteger compute(String arg) { // 經過長時間計算後 try { Thread.sleep(500000); } catch (InterruptedException ie) { } return new BigInteger(arg); } }
版本1
通過HashMap時間復雜度為O(1)的特性以及synchronized保證線程安全來構建緩存
public class MyCacheV1<A,V> implements Computable<A,V>{ private Map<A,V> cache = new HashMap<>(); private Computable computable; public MyCacheV1(Computable computable) { this.computable = computable; } @Override public synchronized V compute(A arg) { V result = cache.get(arg); if (result == null) { result = (V)computable.compute(arg); cache.put(arg,result); } return result; } }
版本2
版本1添加了synchronized,多線程情況下造成性能下降 -> 換成ConcurrentHashMap
public class MyCacheV2<A,V> implements Computable<A,V>{ private Map<A,V> cache = new ConcurrentHashMap<>(); private Computable computable; public MyCacheV2(Computable computable) { this.computable = computable; } @Override public V compute(A arg) { V result = cache.get(arg); if (result == null) { result = (V)computable.compute(arg); cache.put(arg,result); } return result; } }
版本3
版本2先判斷 -> 在計算屬於復合操作而且沒有加鎖導致線程不安全會產生重讀計算。如果遇到計算時間非常長的計算,一旦重復會消耗大量的資源。
解決思路:如果其他線程正在計算目標值,當前線程阻塞直到其它線程計算出結果返回即可。
實現:通過FutureTask的get()方法。如果沒有結果,會阻塞當前線程。
public class MyCacheV3<A,V> implements Computable<A,V>{
private Map<A,FutureTask> cache = new ConcurrentHashMap<>();
private Computable computable;
public MyCacheV3(Computable computable) {
this.computable = computable;
}
@Override
public V compute(A arg) {
Future future = cache.get(arg);
if (future == null) {
FutureTask futureTask = new FutureTask(new Callable() {
@Override
public V call() throws Exception {
return (V)computable.compute(arg);
}
});
// 用到了ConcurrentHashMap的原子操作
future = cache.putIfAbsent(arg,futureTask);
// 二次判斷
if (future == null) {future = futureTask; futureTask.run();}
}
V result = null;
try {
result = (V) future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return result;
}
}
緩存實現思路