【優(yōu)雅代碼】15-guavaCache本地緩存使用及源碼解析
歡迎關(guān)注b站賬號(hào)/公眾號(hào)【六邊形戰(zhàn)士夏寧】熊痴,一個(gè)要把各項(xiàng)指標(biāo)拉滿(mǎn)的男人。該文章已在github目錄收錄。
屏幕前的大帥比和大漂亮如果有幫助到你的話(huà)請(qǐng)順手點(diǎn)個(gè)贊敏弃、加個(gè)收藏這對(duì)我真的很重要笛辟。別下次一定了,都不關(guān)注上哪下次一定宫患。
- 視頻講解
- 可直接運(yùn)行的完整代碼
- 上一篇guava精選方法及eventBus觀察者模式源碼解析
- 下一篇guava布隆過(guò)濾與限流算法源碼解析
1.背景
承接前一篇章的guava精選方法
2.cache
這一塊的功能設(shè)計(jì)真的很精巧刊懈,特別是隊(duì)列的設(shè)計(jì)
2.1使用
@SneakyThrows
public static void cache() {
// 注意兩個(gè)如果一起用有時(shí)候會(huì)有bug
Cache<Integer, Integer> accessBuild = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.SECONDS).build();
Cache<Integer, Integer> writeBuild = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.SECONDS).build();
accessBuild.put(1, 1);
accessBuild.put(2, 2);
writeBuild.put(1, 1);
writeBuild.put(2, 2);
// 輸出1
System.out.println(accessBuild.getIfPresent(1));
// 輸出1
System.out.println(writeBuild.getIfPresent(1));
Thread.sleep(500);
// 輸出2
System.out.println(accessBuild.getIfPresent(2));
Thread.sleep(600);
// 輸出null
System.out.println(accessBuild.getIfPresent(1));
// 輸出2
System.out.println(accessBuild.getIfPresent(2));
// 輸出null
System.out.println(writeBuild.getIfPresent(1));
}
輸出如下
1
1
2
null
2
null
2.2核心源碼詳解
- 構(gòu)造方法
// 整體構(gòu)造鏈相對(duì)簡(jiǎn)單
// build方法
public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
checkWeightWithWeigher();
checkNonLoadingCache();
return new LocalCache.LocalManualCache<>(this);
}
// 給expireAfterAccessNanos賦值失效時(shí)間
public CacheBuilder<K, V> expireAfterAccess(long duration, TimeUnit unit) {
checkState(
expireAfterAccessNanos == UNSET_INT,
"expireAfterAccess was already set to %s ns",
expireAfterAccessNanos);
checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
this.expireAfterAccessNanos = unit.toNanos(duration);
return this;
}
// 給expireAfterWriteNanos賦值失效時(shí)間
public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
checkState(
expireAfterWriteNanos == UNSET_INT,
"expireAfterWrite was already set to %s ns",
expireAfterWriteNanos);
checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
this.expireAfterWriteNanos = unit.toNanos(duration);
return this;
}
- put
// put方法,和老版本的ConcurrentHashMap一樣的設(shè)計(jì)模式娃闲,用segment桶
@Override
public V put(K key, V value) {
checkNotNull(key);
checkNotNull(value);
int hash = hash(key);
return segmentFor(hash).put(key, hash, value, false);
}
V put(K key, int hash, V value, boolean onlyIfAbsent) {
lock();
try {
long now = map.ticker.read();
// **********重點(diǎn)方法:清除過(guò)期內(nèi)容********
preWriteCleanup(now);
int newCount = this.count + 1;
if (newCount > this.threshold) { // ensure capacity
expand();
newCount = this.count + 1;
}
AtomicReferenceArray<ReferenceEntry<K, V>> table = this.table;
int index = hash & (table.length() - 1);
ReferenceEntry<K, V> first = table.get(index);
// Look for an existing entry.
// 這里判斷一下是不是已經(jīng)有同樣的key了
for (ReferenceEntry<K, V> e = first; e != null; e = e.getNext()) {
K entryKey = e.getKey();
if (e.getHash() == hash
&& entryKey != null
&& map.keyEquivalence.equivalent(key, entryKey)) {
// We found an existing entry.
ValueReference<K, V> valueReference = e.getValueReference();
V entryValue = valueReference.get();
if (entryValue == null) {
++modCount;
if (valueReference.isActive()) {
enqueueNotification(
key, hash, entryValue, valueReference.getWeight(), RemovalCause.COLLECTED);
setValue(e, key, value, now);
newCount = this.count; // count remains unchanged
} else {
setValue(e, key, value, now);
newCount = this.count + 1;
}
this.count = newCount; // write-volatile
evictEntries(e);
return null;
} else if (onlyIfAbsent) {
// Mimic
// "if (!map.containsKey(key)) ...
// else return map.get(key);
recordLockedRead(e, now);
return entryValue;
} else {
// clobber existing entry, count remains unchanged
++modCount;
enqueueNotification(
key, hash, entryValue, valueReference.getWeight(), RemovalCause.REPLACED);
setValue(e, key, value, now);
evictEntries(e);
return entryValue;
}
}
}
// Create a new entry.
++modCount;
ReferenceEntry<K, V> newEntry = newEntry(key, hash, first);
// **********重點(diǎn)方法:賦值********
setValue(newEntry, key, value, now);
table.set(index, newEntry);
newCount = this.count + 1;
this.count = newCount; // write-volatile
evictEntries(newEntry);
return null;
} finally {
unlock();
// **********重點(diǎn)方法:調(diào)用監(jiān)聽(tīng)者********
postWriteCleanup();
}
}
- setValue
@GuardedBy("this")
void setValue(ReferenceEntry<K, V> entry, K key, V value, long now) {
// 獲取這個(gè)包裝entry原先的值虚汛,如果原先這個(gè)key不存在,則獲取不到東西
ValueReference<K, V> previous = entry.getValueReference();
int weight = map.weigher.weigh(key, value);
checkState(weight >= 0, "Weights must be non-negative");
ValueReference<K, V> valueReference =
map.valueStrength.referenceValue(this, entry, value, weight);
// 將value寫(xiě)入到entry包裝對(duì)象中
entry.setValueReference(valueReference);
// 核心方法
recordWrite(entry, weight, now);
previous.notifyNewValue(value);
}
@GuardedBy("this")
void recordWrite(ReferenceEntry<K, V> entry, int weight, long now) {
// we are already under lock, so drain the recency queue immediately
drainRecencyQueue();
totalWeight += weight;
if (map.recordsAccess()) {
// 設(shè)置訪問(wèn)時(shí)間
entry.setAccessTime(now);
}
if (map.recordsWrite()) {
// 設(shè)置寫(xiě)時(shí)間
entry.setWriteTime(now);
}
// **************重點(diǎn)方法***************這個(gè)地方將指針存了兩份隊(duì)列到末尾皇帮,因?yàn)榫彺鏁r(shí)間是一致的卷哩,所以只要判斷隊(duì)列頭部就可以了
accessQueue.add(entry);
writeQueue.add(entry);
}
- WriteQueue與accessQueue
這兩個(gè)用的實(shí)現(xiàn)類(lèi)基本一致,這里重寫(xiě)了add方法属拾,重寫(xiě)的目的是如果key一樣的entry就進(jìn)行重排而不是插入
@Override
public boolean offer(ReferenceEntry<K, V> entry) {
// unlink
// 將entry的前一個(gè)和后一個(gè)進(jìn)行互指
connectWriteOrder(entry.getPreviousInWriteQueue(), entry.getNextInWriteQueue());
// add to tail
// entry和對(duì)頭互指将谊,和隊(duì)尾互指冷溶,即添加到隊(duì)尾
connectWriteOrder(head.getPreviousInWriteQueue(), entry);
connectWriteOrder(entry, head);
return true;
}
// 兩個(gè)對(duì)象進(jìn)行互指
static <K, V> void connectWriteOrder(ReferenceEntry<K, V> previous, ReferenceEntry<K, V> next) {
previous.setNextInWriteQueue(next);
next.setPreviousInWriteQueue(previous);
}
- preWriteCleanup
void preWriteCleanup(long now) {
runLockedCleanup(now);
}
void runLockedCleanup(long now) {
if (tryLock()) {
try {
drainReferenceQueues();
// 核心方法繼續(xù)進(jìn)入
expireEntries(now); // calls drainRecencyQueue
readCount.set(0);
} finally {
unlock();
}
}
}
void expireEntries(long now) {
drainRecencyQueue();
ReferenceEntry<K, V> e;
// 就兩個(gè)隊(duì)列不停的判斷頭節(jié)點(diǎn)是不是失效
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();
}
}
}
- 監(jiān)聽(tīng)者模式
void postWriteCleanup() {
// 核心方法繼續(xù)進(jìn)入
runUnlockedCleanup();
}
void runUnlockedCleanup() {
// locked cleanup may generate notifications we can send unlocked
if (!isHeldByCurrentThread()) {
// 核心方法繼續(xù)進(jìn)入
map.processPendingNotifications();
}
}
void processPendingNotifications() {
RemovalNotification<K, V> notification;
// 前面所有涉及notify的操作都會(huì)進(jìn)到相應(yīng)的queue中,然后在該主方法中進(jìn)行回調(diào)
while ((notification = removalNotificationQueue.poll()) != null) {
try {
// 核心流程瓢娜,只要實(shí)現(xiàn)removalListener挂洛,通過(guò)構(gòu)造方法傳進(jìn)來(lái),然后這里就會(huì)同步調(diào)用實(shí)現(xiàn)的回調(diào)方法
// PS第一遍看源碼一度卡在這個(gè)位置眠砾,不知道這玩意兒就一個(gè)通知機(jī)制怎么就移除元素了
removalListener.onRemoval(notification);
} catch (Throwable e) {
logger.log(Level.WARNING, "Exception thrown by removal listener", e);
}
}
}