高效缓存设计的哲学
文章目录
- 引言
- 基于缓存存储运算结果
- 锁分段散列减小锁粒度
- 异步化提升处理效率
- 原子化避免重复运算
- 小结
- 参考
引言
基于缓存存储运算结果
利用缓存避免非必要的计算,提升结果获取速度,但还是存在问题,每个线程都需要等待锁才能看结果和运算:
public final Map<Integer, Integer> cache = new HashMap<>();public synchronized int compute(int arg) {if (cache.containsKey(arg)) {//若存在直接返回结果return cache.get(arg);} else {//若不存在则计算并返回int result = doCompute(arg);cache.put(arg, result);return result;}}private int doCompute(int key) {ThreadUtil.sleep(500);return key << 1;}
锁分段散列减小锁粒度
利用分段锁分散压力,但是运算耗时可能导致重复计算和put操作:
public final Map<Integer, Integer> cache = new ConcurrentHashMap<>();public int compute(int arg) {Integer res = cache.get(arg);if (res == null) {int result = doCpmpute(arg);cache.put(arg, result);}return res;}private int doCpmpute(int arg) {ThreadUtil.sleep(3000);return arg << 1;}
异步化提升处理效率
使用future避免计算的阻塞,当然因为判空和创建任务非原子操作,很可能还是出现重复计算的情况:
public final Map<Integer, FutureTask<Integer>> cache = new ConcurrentHashMap<>();public int compute(int key) throws ExecutionException, InterruptedException {FutureTask<Integer> f = cache.get(key);if (f == null) {FutureTask<Integer> futureTask = new FutureTask<>(() -> doCompute(key));//缓存保证下一个线程看到时直接取出使用cache.put(key, futureTask);futureTask.run();f=futureTask ;}return f.get();}private int doCompute(int arg) {ThreadUtil.sleep(3000);return arg << 1;}
原子化避免重复运算
原子操作避免重复计算,并发运算一个数字时都采用同一个任务的结果
public int compute(int key) throws ExecutionException, InterruptedException {FutureTask<Integer> f = cache.get(key);if (f == null) {FutureTask<Integer> futureTask = new FutureTask<>(() -> doCompute(key));//原子操作添加,若返回空说明第一次添加,则让这个任务启动,其他线程直接基于缓存中的任务获取结果f = cache.putIfAbsent(key, futureTask);if (f == null) {f = futureTask;f.run();}futureTask.run();f = futureTask;}return f.get();}