Java并发编程基础构建模块(06)——高效缓存总结示例
来源:互联网 发布:java 取模 编辑:程序博客网 时间:2024/05/17 02:04
前面介绍完了并发编程的基础构建模块,也就是一些常用的基础类,这里做个简单的例子,很常见的一个功能,缓存,缓存通常情况下看上去比较简单,实际中各种缓存框架已经让我们开发非常方便,但是假如自己新写一个缓存功能呢,是否能做到高效且可伸缩呢?
下面做一个简单缓存,用于改进一个计算费时且复杂的函数。先看一下原始功能:
由于这个工具计算方法传入相同参数的结果也相同(在数学中是普遍的现象,其他情况下不一定),所以可将计算结果缓存起来(放到内存中,也就是放到某个属性里面存着),后续再用到的时候就不用重新计算了。首先想到的是放到一个Map中,结果如下:
因为HashMap不是线程安全的,所以采用了比较保守的方法,整个方法都加上同步了,但这样就带来1个问题,同时只有1个线程能执行计算方法,其他线程都只能阻塞,计算方法时间较长,其他线程阻塞时间就很长。显然不是我们希望得到的结果,我们需要改进一下:
(某些情况下确实可以,比如要计算的参数种类非常少,短时间内就能计算出全部参数的结果了,以后不会有新的参数了,这样前期慢点,后续就没问题了)
上面这段代码,使用了ConcurrentHashMap消除了HashMap线程不安全的方法,也取消了synchronized同步,性能上来了,多线程并发使用也没问题了。但还有一个不足,当两个线程同时传入相同的参数时,会导致重复执行(业务逻辑执行时间越长概率越大),我们希望的结果是相同参数只计算1次,后续再调用就从缓存中获取就行了。
这样就会引出一个问题,想要只计算1次,多线程还要同时获取这个结果怎么办,不由得想到前面介绍过的一个类:FutureTask。FutureTask线程在计算时,所有其他线程使用get方法获取值会阻塞,一旦计算完成,其他线程能马上拿到结果,好,我们改造一下:
这样看似不错了,但也没彻底解决同一个值只执行1次,只是将原来的概率降的非常低而已,假如realCalculate要执行2s,Calculate02中2s内进来的相同方法会都执行,而Calculate03中把时间降到了if(ft== null)中的方法执行时间,也就是说相同值同时执行时,可能同时初始化多个FutureTask,虽然概率很低。防止重复初始化我们自然想到了加锁,看下面的代码:
这个锁有点类似单例模式中的单例初始化的锁,好了,截至目前看似比较完美了,其实仍然有两处不足:
1、 在创建FutureTask前加锁,锁的粒度比较大,同步功能越少对性能越好,其实还有更细的加锁方式,就是使用ConcurrentHashMap内自己的锁;
2、 FutureTask存在计算失败的可能,如果计算线程被取消了怎么办,其他等待线程和后续的线程再执行时,这个参数对应的FutureTask已经损坏了,无法拿到结果了,所以当失败时,需要清除FutureTask,换一个新的。
好,我们来看下面这个代码:
这样看来就完美一些了,这样不仅高效,而且安全性也非常好,扩展性也不错。
其实,再缓存设计中,如果不缓存最终结果,就要控制好缓存对象,否则会造成缓存污染问题,比如上面的程序中,没缓存最终结果,而是缓存了FutureTask,FutureTask会引来一些其他问题,比如发生某些异常时,取消计算时等情况要清除缓存,这样缓存才能计算成功。还有缓存逾期问题,每个结果需要指定一个时间,定期扫描逾期元素并删除等,不过这些是缓存设计方法考虑的,此处例子以并发为主,不做太多讨论。
- Java并发编程基础构建模块(06)——高效缓存总结示例
- Java并发编程基础构建模块(06)——高效缓存总结示例
- Java并发编程学习——基础构建模块
- Java并发编程基础构建模块(02)——并发容器
- Java并发编程基础构建模块(02)——并发容器
- Java并发编程基础构建模块(01)——同步容器类
- Java并发编程基础构建模块(03)——阻塞队列
- Java并发编程基础构建模块(04)——线程阻塞与中断
- Java并发编程基础构建模块(05)——同步工具类
- Java并发编程基础构建模块(01)——同步容器类
- Java并发编程基础构建模块(03)——阻塞队列
- Java并发编程基础构建模块(04)——线程阻塞与中断
- Java并发编程基础构建模块(05)——同步工具类
- Java 并发编程之基础构建模块
- Java 并发编程之基础构建模块 (二)
- 《java并发编程实战》基础构建模块(一)
- 《java并发编程实战》基础构建模块(二)
- 《java并发编程实战》基础构建模块(三)
- httpClient 入门知识点
- C++反汇编->函数调用原理
- 适配器模式理解
- OCM10g考试大纲
- 第六章自动1122徐达武
- Java并发编程基础构建模块(06)——高效缓存总结示例
- request,session,application三者关系<转>
- JavaScript规范之类型
- 第五周作业--有向图的邻接表和反向图的邻接表
- JavaScript规范之对象
- 看完书要及时消化(1)《暗时间》
- Android Performance(6) Pixel Perfect
- JavaScript规范之数组
- MFC入门学习之控件(6)标签控件List Control的添加和使用(下)