博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Cglib中LoadingCache源码分析
阅读量:6454 次
发布时间:2019-06-23

本文共 3172 字,大约阅读时间需要 10 分钟。

  在实际的开发过程中,我们经常需要用到缓存。使用缓存常见的一个场景就是key不在缓存中,这个时候我们会去读取这个key对应的值,然后把这个值放到缓存中,代码如下:

public class CacheNoFuture {    private ConcurrentHashMap
cache = new ConcurrentHashMap<>(); public Object get(String key) { Object o = cache.get(key); if (o == null) { o = readFromDB(key); cache.put(key, o); } return o; } private Object readFromDB(String key) { return new Object(); }复制代码

  这个代码有一个比较大的问题就是:如果同一时刻大量的请求发现o是空,都会去调用readFromDB,导致缓存被击穿了,可能的后果就是数据库直接被冲垮。理想的情况是同一个key同一时间只有一个thread去调用readFromDB,其他的thread等待它的结果。我们看一下Cglib包下面的LoadingCache是怎么做的。

1.代码位置

目前我使用的cglib的maven配置如下:

cglib
cglib
3.2.5
复制代码

LoadingCache代码在net.sf.cglib.core.internal包下面。

2.代码分析

  先看一下它的get方法,还是比较好理解的,有点不一样的是它判断了从map里面取到的内容是不是FutureTask,这个会在后面介绍。接下来我们看一下当从缓存里面读到的数据为空或者为FutureTask的时候,它做了什么。

public V get(K key) {    final KK cacheKey = keyMapper.apply(key);    Object v = map.get(cacheKey);    if (v != null && !(v instanceof FutureTask)) {        return (V) v;    }    return createEntry(key, cacheKey, v);}复制代码

  这段代码还是比较好理解的,我觉得理一下我标注的5行基本就差不多了。

  • line 1: 从 get 我们知道进入createEntry的条件是v不为空并且v不是FutureTask ,这一行判断v不为空,那只能说明v是FutureTask,所以把v赋值给task,表示目前已经有一个线程在加载数据了。
  • line 2: 很多线程正在竞争的去加载数据,但是只有putIfAbsent返回为空的那个成为creator
  • line 3、4: 结合line 2的解释,没有竞争成功的,会获得creatorFutureTask(加载没有完成)或者V(加载已经完成)
  • line 5:加载完成之后放回到cache里面,这也就是为什么有line 4的原因了。
protected V createEntry(final K key, KK cacheKey, Object v) {        FutureTask
task; boolean creator = false; if (v != null) { //line 1 // Another thread is already loading an instance task = (FutureTask
) v; } else { task = new FutureTask
(new Callable
() { public V call() throws Exception { return loader.apply(key); } }); Object prevTask = map.putIfAbsent(cacheKey, task); if (prevTask == null) { //line 2 // creator does the load creator = true; task.run(); } else if (prevTask instanceof FutureTask) { //line 3 task = (FutureTask
) prevTask; } else { //line 4 return (V) prevTask; } } V result; try { result = task.get(); } catch (InterruptedException e) { throw new IllegalStateException("Interrupted while loading cache item", e); } catch (ExecutionException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw ((RuntimeException) cause); } throw new IllegalStateException("Unable to load cache item", cause); } if (creator) { //line 5 map.put(cacheKey, result); } return result; }复制代码

3.总结

  这种做法也有明显的不足:

  • 加载成功之后,key对应的值就不会再变了,即使我们数据源头发生了变化。

转载地址:http://wafzo.baihongyu.com/

你可能感兴趣的文章
IntelliJ IDEA 注册码
查看>>
linux 上面配置apache2的虚拟目录
查看>>
Linux学习总结 (未完待续...)
查看>>
NoSQL数据库探讨 - 为什么要用非关系数据库?
查看>>
String字符串的截取
查看>>
switch函数——Gevent源码分析
查看>>
Spring MVC简单原理
查看>>
DynamoDB Local for Desktop Development
查看>>
ANDROID的SENSOR相关信息
查看>>
laravel 使用QQ邮箱发送邮件
查看>>
用javascript验证哥德巴赫猜想
查看>>
Shell编程-环境变量配置文件
查看>>
thymeleaf 中文乱码问题
查看>>
(转)CSS浮动(float,clear)通俗讲解
查看>>
os.walk函数
查看>>
[Unity3d]DrawCall优化手记
查看>>
细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一
查看>>
SQL Serever学习7——数据表2
查看>>
洛谷——P2404 自然数的拆分问题
查看>>
(转)Mac 下设置android NDK的环境
查看>>