Официальный адрес документа:GitHub.com/Google/Мелон В…
использовать
1. Строительство
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
// 必须实现
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
// 可以实现loadAll, reload
});
скопировать код
2. Получить
- get
会要求你try住 load中的异常
try {
return graphs.get(key);
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
скопировать код
-
graphs.getUnchecked(key);
-
Массовое приобретение
getAll(Iterable<? extends K> keys) throws ExecutionException;
这里需要在CacheBuilder定义CacheLoader时候,实现 loadAll
скопировать код
- Пользовательский метод загрузки
V get(K, Callable<V>);
cache.get(key, new Callable<Value>() {
@Override
public Value call() throws AnyException {
return doThingsTheHardWay(key);
}
});
скопировать код
3. Обмен
А. Недействительно в соответствии с максимальным размером или максимальным весом.
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(100)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return createExpensiveGraph(key);
}
});
或者
.maximumWeight(100000)
.weigher(new Weigher<Key, Graph>() {
public int weigh(Key k, Graph g) {
return g.vertices().size();
}
})
不能同时设置maxsize 和 maxweight
换出时采用LRU策略,accessQueue
скопировать код
Здесь нужно отметить два момента
- Вес вычисляется только тогда, когда он устанавливается в кеш в первый раз, и не будет меняться в зависимости от изменения значения и ключа.
- Не строго установлен в соответствии с maxsize
Поскольку LoadingCache похож на ConcurrentHashmap версии 1.6, он разделен на множество сегментов для обслуживания.
Во время инициализации maxSize будет поддерживаться для каждого сегмента, а выгрузка поддерживается каждым сегментом.
б. Истекает или обновляется в зависимости от времени
expireAfterAccess(long, TimeUnit)
expireAfterWrite(long, TimeUnit)
refreshAfterWrite(long, TimeUnit)
скопировать код
c. Срок действия истекает из-за аннулирования ссылки на объект.
CacheBuilder.weakKeys()
CacheBuilder.weakValues()
CacheBuilder.softValues()
скопировать код
г. Измените метод мониторинга
CacheBuilder
.removalListener(new RemovalListener<String, InnerValue>() {
@Override
public void onRemoval(RemovalNotification<String, InnerValue> notification) {
System.out.println("remove " + notification.getKey());
}
})
скопировать код
4. Очистить
Мета аннулирования времени очистки:
- когда пишешь
- Или, если не было операции записи (64 операции чтения после операции записи), она будет очищена операцией чтения.
Если вы хотите, чтобы фоновый поток регулярно очищался, вы можете периодически вызывать Cache.cleanUp() отдельным потоком.
5. Чтение исходного кода
Базовая структура аналогична ConcurrentHashMap версии 1.6: используется сегмент, а затем он делится на разные узлы.
- ссылка признана недействительной gc
为了支持虚指针,软指针回收
Entry在hashMap中就是只需要保存Key,Value
这里Key,Value都可能被回收调,所以都用Reference对象
并且维护gc后的队列,来用于gc
keyReferenceQueue
valueReferenceQueue
而需要通过value找到对应的节点,所以每个value 需要维护一个指向entry的指针
скопировать код
- Время истечения срока действия поддержки и замена LRU
维护3个队列
- Queue<ReferenceEntry<K, V>> writeQueue; 最近写的elements
- Queue<ReferenceEntry<K, V>> accessQueue; 最近访问的elements
- Queue<ReferenceEntry<K, V>> recencyQueue 最近读取的队列
前两个队列都是双向链表(最后连成一个环)实现
每一个Entry都维护前后节点,最新访问和最新写入的element放在最后
非线程安全
只有加锁之后才能操作
recencyQueue 是一个 ConcurrentLinkedQueue。
有读操作的时候,会add到这个queue
当需要clear,或者写入的时候,会把recencyQueue中的数据添加到accessQueue队列中,再进行处理
скопировать код
- процесс загрузки
调用load的时候,会加锁,然后生成一个LoadingValueReference临时节点放在table里
而如果refrsh的时候,会先返回之前的值
load结束之后,会entry中的value修改为真实的reference
- 其他线程
而load过程中,其他的线程get时会调用waitForLoadingValue
相当于调用了 Future.get,这个future是load返回的Future,在Future结束之后会把线程换气
V waitForLoadingValue(ReferenceEntry<K, V> e, K key, ValueReference<K, V> valueReference)
throws ExecutionException {
***
V value = valueReference.waitForValue();
***
}
public V waitForValue() throws ExecutionException {
return getUninterruptibly(futureValue);
}
public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException {
***
return future.get();
***
}
скопировать код
Проблема с обновлением
Refresh вызовет асинхронный loadAsync и вызовет перезагрузку CacheLoader.По умолчанию reload также вызывает синхронную загрузку, поэтому асинхронная логика отсутствует.
Вы можете реализовать перезагрузку и использовать другие потоки для возврата ListenableFuture.
new CacheLoader<String, InnerValue>() {
@Override
public InnerValue load(String key) throws Exception {
InnerValue result = new InnerValue();
result.value = "" + key + "_" + System.currentTimeMillis();
return result;
}
@Override
public ListenableFuture<InnerValue> reload(String key, InnerValue oldValue) throws Exception {
return service.submit(new Callable<InnerValue>() {
@Override
public InnerValue call() throws Exception {
Thread.sleep(1000);
return load(key);
}
});
}
});
скопировать код
При таком вызове обновления основной поток не будет зависать.
Уведомление:
- Если другой поток устанавливает значение после вызова обновления, поток обновления не перезапишет значение снова после завершения выполнения.
- Если другой поток удалит это значение после обновления, или эта запись была gc, поток обновления все равно добавит это значение в таблицу после завершения выполнения.
разное
Вместо этого используется локальный кеш Spring5Caffeine