一种高效可伸缩的缓存设计方法
来源:互联网 发布:单片机继电器怎么使用 编辑:程序博客网 时间:2024/03/29 12:55
几乎所有的服务器应用中都要使用缓存,重用之前的计算结果能降低延迟,提高吞吐量,但是要消耗更多的内存。
Memorizer1简单地使用HashMap来缓存之前的计算结果:
public interface Computable<A,V> {V compute(A arg) throws InterruptedException;}
public class Memorizer1<A,V> implements Computable<A,V> {private final Map<A,V> cache=new HashMap<A,V>();private final Computable<A,V> c;public Memorizer1(Computable<A,V> c){this.c=c;}@Overridepublic synchronized V compute(A arg) throws InterruptedException {V result=cache.get(arg);if(result==null){result=c.compute(arg);cache.put(arg, result);}return result;}}
由于HashMap不是线程安全的,所以Memorizer1采用了一种保守的策略:对整个compute方法进行同步。并行性很低。
用ConcurrentHashMap来代替HashMap就不需要对compute方法进行同步了。这便有了Memorizer2:
public class Memorizer2<A,V> implements Computable<A,V> {private final Map<A,V> cache=new ConcurrentHashMap<A,V>();private final Computable<A,V> c;public Memorizer1(Computable<A,V> c){this.c=c;}@Overridepublic V compute(A arg) throws InterruptedException {V result=cache.get(arg);if(result==null){result=c.compute(arg);cache.put(arg, result);}return result;}}Memorizer2存在的问题是:当某个线程启动了一个开销很大的计算,而其他线程并不知道这个计算正在进行,那么很可能会重复这个计算。
Memorizer4首先检查某个相应的计算是否已经启动(与Memorizer2不同,它是首先检查某个计算是否已经完成)。如果还没有启动,就创建一个FutureTask,并注册到Map中,然后启动计算;如果已经启动,那就等待现有计算的结果。
public class Memorizer3<A, V> implements Computable<A, V> {private final Map<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();private final Computable<A, V> c;public Memorizer1(Computable<A, V> c) {this.c = c;}@Overridepublic V compute(final A arg) throws InterruptedException {Future<V> f = cache.get(arg);if (f == null) {Callable<V> eval = new Callable<V>() {@Overridepublic V call() throws Exception {return c.compute(arg);}};FutureTask<V> ft = new FutureTask<V>(eval);f = ft;cache.put(arg, ft); // 把FutureTask放入Mapft.run(); // 启动计算}try {return f.get(); // 等待计算完成} catch (ExecutionException e) {throw launderThrowable(e.getCause());}}// 对各类异常分别进行处理private RuntimeException launderThrowable(Throwable cause) {if (cause instanceof RuntimeException)return (RuntimeException) cause;else if (cause instanceof Error)throw (Error) cause;elsethrow new IllegalStateException("Not unckecked", cause);}}
同Memorizer2一样,Memorizer3仍然有可能导致同样的计算重复进行,当然这个可能性比Memorizer2小很多。由于在compute方法采用了“先检查再执行”操作,有可能两个线程在检查时都发现相应的FutureTask不在Map中,导致重复的计算。
Memorizer4使用了ConcurrentMap的原子操作putIfAbsent,避免了Memorizer3的漏洞。
public class Memorizer4<A, V> implements Computable<A, V> {private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();private final Computable<A, V> c;public Memorizer1(Computable<A, V> c) {this.c = c;}@Overridepublic V compute(final A arg) throws InterruptedException {while (true) {Future<V> f = cache.get(arg);if (f == null) {Callable<V> eval = new Callable<V>() {@Overridepublic V call() throws Exception {return c.compute(arg);}};FutureTask<V> ft = new FutureTask<V>(eval);f = cache.putIfAbsent(arg, ft);if (f == null) {f = ft;ft.run();}}try {return f.get(); // 等待计算完成} catch (CancellationException e) {cache.remove(arg, f); //如果计算没有完成Task就取消了,那它应该从Map中移除。} catch (ExecutionException e) {throw launderThrowable(e.getCause());}}}// 对各类异常分别进行处理private RuntimeException launderThrowable(Throwable cause) {if (cause instanceof RuntimeException)return (RuntimeException) cause;else if (cause instanceof Error)throw (Error) cause;elsethrow new IllegalStateException("Not unckecked", cause);}}注意putIfAbsent是在ConcurrentMap类中定义的方法,所以这次的cache声明时是ConcurrentMap,而非Map。
- 一种高效可伸缩的缓存设计方法
- 高效且可伸缩的结果缓存
- 构建高效可伸缩的结果缓存
- 构建高效且可伸缩的结果缓存
- 构建高效且可伸缩的结果缓存引申的并发测试规范化
- Java并发(具体实例)——构建高效且可伸缩的结果缓存
- 【多线程_提高篇】 创建高效且可伸缩的结果缓存
- 一头扎进多线程-构建高效且可伸缩的结果缓存
- java并发——构建高效且可伸缩的结果缓存
- java多线程(十) 之 构建高效且可伸缩的结果缓存
- java并发编程实战-构建高效且可伸缩的结果缓存
- 设计高效SQL: 一种视觉的方法
- 一种可展开伸缩的tableView实现
- [转]设计可伸缩的应用程序
- 可伸缩的系统设计模式
- 可伸缩系统的设计模式
- 谈如何设计可伸缩的代码?
- 高可伸缩的分库分表设计
- [CentOS 6.4 ] 安装VMware Tools
- Android工程中R.java文件丢失重建
- 手把手教你玩转SOCKET模型之重叠I/O篇(下)
- android笔记
- Python实现:斐波那契数列、交换两个变量的值、从一组数据中找出最大最小值
- 一种高效可伸缩的缓存设计方法
- 教务系统开发总结
- Struts2入门到精通十三——————总结
- 设计模式—外观模式
- python是在Prototype模式和Singleton模式
- android笔记2
- Java--二进制正负表示
- 手把手教你玩转SOCKET模型之重叠I/O篇(上)
- 点结构体于枚举