结合 ConcurrentHashMap.putIfAbsent 与 Futrue 实现本地缓存防击穿
http://blog.csdn.net/michaelwubo/article/details/50865185
Java 高并发缓存与Guava Cache
这篇文章名字叫《Java 高并发缓存与Guava Cache》,但最核心的是如何高效的防止本地缓存击穿
业务模型:
res = cache.get(key); if(res == null) { value = sql; cach.put(key, value); return value; } return res;
这个代码多线程里不行:
在多线程时,出现了在缓存里没有缓存时,会执行一样执行多次的业务数据并返回处理的数据,我们分析一下出现这种情况的:
(1)当线程T1访问cacheMap里面有没有,这时根据业务到后台处理业务数据并返回处理数据,并放入缓存。
(2)当线程T2访问cacheMap里面同样也没有,也把根据业务到后台处理业务数据并返回处理数据,并放入缓存。
首先想到的是同步Synchronized:
res = cache.get(key); if(res == null) { synchronized(this) { oldValue = cache.get(key); if(oldValue == null) { value = sql; cach.put(key, value); return value; } else return oldValue; } } return res;然后就会想到这个代码对性能损伤较大,而且不适用于集群环境。
缓存失效的瞬间,并发线程全部阻塞
然后重点来了:使用Future和ConcurrentMap异步更新缓存
res = cache.get(key); if(res == null) { FutureTask<V> futureTask=new FutureTask<V>(callable); futureValue=cacheMap.putIfAbsent(keyValue, futureTask); if(futureValue==null){ futureValue=futureTask; futureTask.run(); } return futureValue.get(); } return res;假设现在缓存失效,并发线程5个,都创建了FutureTask,
putIfAbsent:如果(调用该方法时)key-value 已经存在,则返回那个 value 值。如果调用时 map 里没有找到 key 的 mapping,返回一个 null 值
故最终5个中只有一个线程能够得到真正的 执行-阻塞-返回 过程,其它4个线程可能有两种情况:
(1)阻塞-返回
(2)直接返回
比用syn同步块清一色阻塞要好得多了