Guava1.2——GuavaCache的RemoveListener分析

put操作


1.prewrite

void preWriteCleanup(long now)

void runLockedCleanup(long now)

void drainReferenceQueues()

void expireEntries(long now)

以上是准备工作,和get方法中的准备类似。

2.postwrite

void postWriteCleanup()

void runUnlockedCleanup();

// 该方法会把removalNotificationQueue中的元素清楚掉,并调用onRemoval方法

void processPendingNotifications(){
RemovalNotification<K, V> notification;
while ((notification = removalNotificationQueue.poll()) != null) {
try {
removalListener.onRemoval(notification);
} catch (Throwable e) {
logger.log(Level.WARNING, "Exception thrown by removal listener", e);
}
}
}

所以真正调用OnRemoval方法是在put操作中调用的。

get操作


1.pre read

其实不像没有pre read方法,只是为了对应pre write。

V get(Object key, int hash)

ReferenceEntry<K, V> getLiveEntry(Object key, int hash, long now)

void tryExpireEntries(long now)

void expireEntries(long now) {
drainRecencyQueue();

ReferenceEntry<K, V> e;
while ((e = writeQueue.peek()) != null && map.isExpired(e, now)) {
if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
throw new AssertionError();
}
}
while ((e = accessQueue.peek()) != null && map.isExpired(e, now)) {
if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {
throw new AssertionError();
}
}
}

boolean removeEntry(ReferenceEntry<K, V> entry, int hash, RemovalCause cause) ;

ReferenceEntry<K, V> removeValueFromChain(ReferenceEntry<K, V> first,
ReferenceEntry<K, V> entry, @Nullable K key, int hash, ValueReference<K, V> valueReference,
RemovalCause cause) {
enqueueNotification(key, hash, valueReference, cause);
writeQueue.remove(entry);
accessQueue.remove(entry);

if (valueReference.isLoading()) {
valueReference.notifyNewValue(null);
return first;
} else {
return removeEntryFromChain(first, entry);
}
}

ReferenceEntry<K, V> removeValueFromChain(ReferenceEntry<K, V> first,
ReferenceEntry<K, V> entry, @Nullable K key, int hash, ValueReference<K, V> valueReference,
RemovalCause cause) {
enqueueNotification(key, hash, valueReference, cause);
writeQueue.remove(entry);
accessQueue.remove(entry);

if (valueReference.isLoading()) {
valueReference.notifyNewValue(null);
return first;
} else {
return removeEntryFromChain(first, entry);
}
}

// 向removalNotificationQueue队列中插入元素,在调用OnRemoval的时候会删除这些元素

void enqueueNotification(@Nullable K key, int hash, ValueReference<K, V> valueReference,
RemovalCause cause) {
totalWeight -= valueReference.getWeight();
if (cause.wasEvicted()) {
statsCounter.recordEviction();
}
if (map.removalNotificationQueue != DISCARDING_QUEUE) {
V value = valueReference.get();
RemovalNotification<K, V> notification = new RemovalNotification<K, V>(key, value, cause);
map.removalNotificationQueue.offer(notification);
}
}

2.post read

void postReadCleanup() {
if ((readCount.incrementAndGet() & DRAIN_THRESHOLD) == 0) {
cleanUp();
}
}

void cleanUp() {
long now = map.ticker.read();
runLockedCleanup(now);
runUnlockedCleanup();
}

3.解释

其实,在get方法调用的时候,已经过期的value已经取不到了,但是要是想让OnRemoval响应,还是要等下一次put才能生效,原因见put方法解释。

要涉及键值的读操作,都将执行postReadCleanup操作,每次执行postReadCleanup操作时readCount都增1,当其达到64时(DRAIN_THRESHOLD为0x3F,即0011 1111),引发cleanUp操作,也会调用OnRemoval方法。

也就是说postRead和postWrite会操作OnRemoval方法。

示例


Cache<String, Integer> cache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(2, TimeUnit.SECONDS)
.recordStats()
.removalListener(new RemovalListener<String, Integer>() {
@Override
public void onRemoval(RemovalNotification<String, Integer> rn) {
System.out.println("被移除的key:" + rn.getKey() + ", 原因:" + rn.getCause());
}
})
.build();

cache.put("a", 100);
Thread.sleep(4000);
cache.getIfPresent("a");
cache.put("c", 300);

如果希望OnRemoval方法被调用,那么红色的两行缺一不可。首先get方法会将已经过期的a-100键值对塞入删除队列,即queue的offer操作,然后put方法会触发queue的poll操作,如果取出来元素的话,就执行OnRemoval操作

while ((notification = removalNotificationQueue.poll()) != null) {
try {
removalListener.onRemoval(notification);

...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值