Guava Cache缓存设计原理

直接从该链中移除该节点

poll操作:

将头节点的下一个节点移除,并返回。

缓存相关操作


Segment的evict清除策略


在每次调用操作的开始和结束时触发清理工作,这样比一般的缓存另起线程监控清理相比,可减少开销。

但若长时间没有调用方法,会导致不能及时清理释放内存空间。

evict主要处理四个Queue:

  • keyReferenceQueue

  • valueReferenceQueue

  • writeQueue

  • accessQueue

前两个queue是因为WeakReference、SoftReference被垃圾回收时加入的,清理时只需遍历整个queue,将对应项从LocalCache移除即可:

  • keyReferenceQueue存放ReferenceEntry

  • valueReferenceQueue存放的是ValueReference

要从Cache中移除需要有key,因而ValueReference需要有对ReferenceEntry的引用。

后两个Queue,只需检查是否配置了相应的expire时间,然后从头开始查找已经expire的Entry,将它们移除。

Segment中的put操作:put操作相对比较简单,首先它需要获得锁,然后尝试做一些清理工作,接下来的逻辑类似ConcurrentHashMap中的rehash,查找位置并注入数据。需要说明的是当找到一个已存在的Entry时,需要先判断当前的ValueRefernece中的值事实上已经被回收了,因为它们可以是WeakReference、SoftReference类型,如果已经被回收了,则将新值写入。并且在每次更新时注册当前操作引起的移除事件,指定相应的原因:COLLECTED、REPLACED等,这些注册的事件在退出的时候统一调用Cache注册的RemovalListener,由于事件处理可能会有很长时间,因而这里将事件处理的逻辑在退出锁以后才做。最后,在更新已存在的Entry结束后都尝试着将那些已经expire的Entry移除。另外put操作中还需要更新writeQueue和accessQueue的语义正确性。

Segment带CacheLoader的get操作

V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {

checkNotNull(key);

checkNotNull(loader);

try {

if (count != 0) { // read-volatile

// don’t call getLiveEntry, which would ignore loading values

ReferenceEntry<K, V> e = getEntry(key, hash);

if (e != null) {

long now = map.ticker.read();

V value = getLiveValue(e, now);

if (value != null) {

recordRead(e, now);

statsCounter.recordHits(1);

return scheduleRefresh(e, key, hash, value, now, loader);

}

ValueReference<K, V> valueReference = e.getValueReference();

if (valueReference.isLoading()) {

return waitForLoadingValue(e, key, valueReference);

}

}

}

// at this point e is either null or expired;

return lockedGetOrLoad(key, hash, loader);

} catch (ExecutionException ee) {

Throwable cause = ee.getCause();

if (cause instanceof Error) {

throw new ExecutionError((Error) cause);

} else if (cause instanceof RuntimeException) {

throw new UncheckedExecutionException(cause);

}

throw ee;

} finally {

postReadCleanup();

}

}

1. 先查找table中是否已存在没有被回收、也没有expire的entry,如果找到,并在CacheBuilder中配置了refreshAfterWrite,并且当前时间间隔已经操作这个事件,则重新加载值,否则,直接返回原有的值

2. 如果查找到的ValueReference是LoadingValueReference,则等待该LoadingValueReference加载结束,并返回加载的值

3. 如果没有找到entry,或者找到的entry的值为null,则加锁后,继续在table中查找已存在key对应的entry,如果找到并且对应的entry.isLoading()为true,则表示有另一个线程正在加载,因而等待那个线程加载完成,如果找到一个非null值,返回该值,否则创建一个LoadingValueReference

并调用loadSync加载相应的值

在加载完成后,将新加载的值更新到table中,即大部分情况下替换原来的LoadingValueReference

CacheBuilder

===========================================================================

提供Builder模式的CacheBuilder生成器来创建缓存。

各个缓存参数的配置设置,类似函数式编程,可自行设置各类参数选型。

Cache<String,String> cache = CacheBuilder.newBuilder()

.maximumSize(1024)

.expireAfterWrite(60,TimeUnit.SECONDS)

.weakValues() .build();

cache.put(“word”,“Hello Guava Cache”);

System.out.println(cache.getIfPresent(“word”));

它提供三种方式加载到缓存:

  1. 在构建缓存的时候,使用build方法内部调用CacheLoader方法加载数据;

  2. callable 、callback方式加载数据;

  3. 直接Cache.put 加载数据,但自动加载是首选的,因为它更容易推断所有缓存内容的一致性

build生成器的两种方式都实现了一种逻辑:

从缓存中取key的值,如果该值已经缓存过了则返回缓存中的值,如果没有缓存过可以通过某个方法来获取这个值

CacheLoader

针对整个cache定义的,可认为是统一的根据K load V的方法:

callable

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值