结合 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同步块清一色阻塞要好得多了



posted on 2018-01-18 15:57  silyvin  阅读(258)  评论(0编辑  收藏  举报