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);
...