предисловие
над "Анализ исходного кода Guava (принцип кэширования)проанализировано вGuava Cache
родственные принципы.
упоминается в концеМеханизм рециркуляции, уведомление о времени удаленияДругой контент, многие друзья тоже очень заинтересованы, на этот раз я буду анализировать и анализировать эти два контента.
Прежде чем начать, давайте изучим две функции, которые поставляются с Java, и у Guava есть определенные приложения.
Ссылки в Java
Ява в первую очередьЦитировать.
Общая JVM раньше была основана наАлгоритмы анализа достижимостиНайдите объект, который необходимо переработать, и оцените, является ли статус выживания объекта таким же, как引用
Связанный.
До JDK 1.2 это было очень просто: состояние объекта было толькоЦитироватьа такжене цитируетсяДва отличия.
Такое деление не очень удобно для сборки мусора, потому что всегда будут какие-то объекты, состояние которых находится между ними.
Поэтому после версии 1.2 были добавлены четыре новых состояния для более тонкого разделения ссылочных отношений:
- Сильная ссылка: этот тип объекта является наиболее распространенным, например, **
A a = new A();
** Это типичная сильная ссылка; такая сильная ссылочная связь не может быть удалена сборщиком мусора. - Мягкая ссылка: такая ссылка указывает на какой-то полезный, но ненужный объект, который необходимо удалить, прежде чем произойдет сборка мусора.
- Слабая ссылка (Weak Reference): это ссылочное отношение, которое слабее, чем мягкая ссылка, а также хранит второстепенные объекты. При сборке мусора такие объекты собираются независимо от того, достаточно ли текущей памяти.
- Фантомная ссылка: это самая слабая ссылочная связь, и получить объект по ссылке невозможно, его единственная функция — уведомлять, когда он перерабатывается.
обратный вызов события
Обратный вызов события на самом деле является распространенным шаблоном проектирования, таким как упомянутый ранее.NettyИспользуется эта конструкция.
Вот демо, попробуйте следующие функции:
- Звонящий задает уведомителю вопрос.
- Способ задавать вопросы асинхронный, а затем делать что-то еще.
- Уведомитель получает проблему и выполняет расчет, а затем перезванивает вызывающей стороне, чтобы сообщить результат.
Обратные вызовы реализованы с использованием интерфейсов в Java, поэтому вам необходимо определить интерфейс:
public interface CallBackListener {
/**
* 回调通知函数
* @param msg
*/
void callBackNotify(String msg) ;
}
Вызывающий вызывает Notifier для выполнения вопроса и передает интерфейс при вызове:
public class Caller {
private final static Logger LOGGER = LoggerFactory.getLogger(Caller.class);
private CallBackListener callBackListener ;
private Notifier notifier ;
private String question ;
/**
* 使用
*/
public void call(){
LOGGER.info("开始提问");
//新建线程,达到异步效果
new Thread(new Runnable() {
@Override
public void run() {
try {
notifier.execute(Caller.this,question);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
LOGGER.info("提问完毕,我去干其他事了");
}
//隐藏 getter/setter
}
Notifier получает вопрос, выполняет вычисления (операция, занимающая много времени) и, наконец, отвечает (интерфейс обратного вызова, сообщающий вызывающей стороне результат).
public class Notifier {
private final static Logger LOGGER = LoggerFactory.getLogger(Notifier.class);
public void execute(Caller caller, String msg) throws InterruptedException {
LOGGER.info("收到消息=【{}】", msg);
LOGGER.info("等待响应中。。。。。");
TimeUnit.SECONDS.sleep(2);
caller.getCallBackListener().callBackNotify("我在北京!");
}
}
Имитация выполнения:
public static void main(String[] args) {
Notifier notifier = new Notifier() ;
Caller caller = new Caller() ;
caller.setNotifier(notifier) ;
caller.setQuestion("你在哪儿!");
caller.setCallBackListener(new CallBackListener() {
@Override
public void callBackNotify(String msg) {
LOGGER.info("回复=【{}】" ,msg);
}
});
caller.call();
}
Окончательный результат выполнения:
2018-07-15 19:52:11.105 [main] INFO c.crossoverjie.guava.callback.Caller - 开始提问
2018-07-15 19:52:11.118 [main] INFO c.crossoverjie.guava.callback.Caller - 提问完毕,我去干其他事了
2018-07-15 19:52:11.117 [Thread-0] INFO c.c.guava.callback.Notifier - 收到消息=【你在哪儿!】
2018-07-15 19:52:11.121 [Thread-0] INFO c.c.guava.callback.Notifier - 等待响应中。。。。。
2018-07-15 19:52:13.124 [Thread-0] INFO com.crossoverjie.guava.callback.Main - 回复=【我在北京!】
Такой смоделированный обратный вызов асинхронного события завершается.
Использование гуавы
Guava использует две вышеупомянутые функции для достиженияПереработка цитата такжеУведомление об удалении.
Цитировать
Можно использовать при инициализации кеша:
- CacheBuilder.weakKeys()
- CacheBuilder.weakValues()
- CacheBuilder.softValues()
Для определения эталонной связи между ключами и значениями.
В приведенном выше анализе видно, что кэшReferenceEntry
Это похоже на Entry HashMap для хранения данных.
Давайте посмотрим на определение ReferenceEntry:
interface ReferenceEntry<K, V> {
/**
* Returns the value reference from this entry.
*/
ValueReference<K, V> getValueReference();
/**
* Sets the value reference for this entry.
*/
void setValueReference(ValueReference<K, V> valueReference);
/**
* Returns the next entry in the chain.
*/
@Nullable
ReferenceEntry<K, V> getNext();
/**
* Returns the entry's hash.
*/
int getHash();
/**
* Returns the key for this entry.
*/
@Nullable
K getKey();
/*
* Used by entries that use access order. Access entries are maintained in a doubly-linked list.
* New entries are added at the tail of the list at write time; stale entries are expired from
* the head of the list.
*/
/**
* Returns the time that this entry was last accessed, in ns.
*/
long getAccessTime();
/**
* Sets the entry access time in ns.
*/
void setAccessTime(long time);
}
Содержит множество часто используемых операций, таких как ссылка на значение, ссылка на ключ, время доступа и т. д.
согласно сValueReference<K, V> getValueReference();
Реализация:
Различные реализации с сильными и слабыми ссылками.
То же самое верно и для ключей:
При использовании этой конструкции слабоссылочные ключи и значения удаляются сборщиком мусора.
Конечно, мы также можем явно переработать:
/**
* Discards any cached value for key {@code key}.
* 单个回收
*/
void invalidate(Object key);
/**
* Discards any cached values for keys {@code keys}.
*
* @since 11.0
*/
void invalidateAll(Iterable<?> keys);
/**
* Discards all entries in the cache.
*/
void invalidateAll();
Перезвоните
Изменил предыдущий пример:
loadingCache = CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.SECONDS)
.removalListener(new RemovalListener<Object, Object>() {
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
LOGGER.info("删除原因={},删除 key={},删除 value={}",notification.getCause(),notification.getKey(),notification.getValue());
}
})
.build(new CacheLoader<Integer, AtomicLong>() {
@Override
public AtomicLong load(Integer key) throws Exception {
return new AtomicLong(0);
}
});
Результаты:
2018-07-15 20:41:07.433 [main] INFO c.crossoverjie.guava.CacheLoaderTest - 当前缓存值=0,缓存大小=1
2018-07-15 20:41:07.442 [main] INFO c.crossoverjie.guava.CacheLoaderTest - 缓存的所有内容={1000=0}
2018-07-15 20:41:07.443 [main] INFO c.crossoverjie.guava.CacheLoaderTest - job running times=10
2018-07-15 20:41:10.461 [main] INFO c.crossoverjie.guava.CacheLoaderTest - 删除原因=EXPIRED,删除 key=1000,删除 value=1
2018-07-15 20:41:10.462 [main] INFO c.crossoverjie.guava.CacheLoaderTest - 当前缓存值=0,缓存大小=1
2018-07-15 20:41:10.462 [main] INFO c.crossoverjie.guava.CacheLoaderTest - 缓存的所有内容={1000=0}
Видно, что при удалении кеша наша кастомная функция будет вызвана обратно и будет сообщена причина удаления.
Так как же Гуава это делает?
По данным LocalCachegetLiveValue()
Когда будет установлено, что срок действия кеша истек, он будет следовать отношениям вызова здесь:
removeValueFromChain()
середина:
enqueueNotification()
Метод перенесет повторно использованный кеш (включая ключ, значение) и причину повторного использования в ранее определенный интерфейс события и добавит его влокальная очередьсередина.
Таким образом, при инициализации не возникает события обратного вызова.
Однако учащиеся, которые использовали очереди, должны быть в состоянии догадаться, что, поскольку очереди написаны здесь, должно быть потребление.
Вернемся к кэшу:
Выполнено в наконецpostReadCleanup()
метод; на самом деле, он потребляет очередь только сейчас:
Если вы продолжите следить, вы обнаружите, что очередь здесь потребляется, а ранее упакованное сообщение об удалении вызывается для нашего пользовательского события, таким образом завершая обратный вызов события.
Суммировать
Весь приведенный выше исходный код:
Анализируя исходный код Guava, мы можем изучить лучшие методы проектирования и реализации и даже попытаться написать его самостоятельно.
В Гуаве еще много мощных улучшений, которые стоит изучить еще раз.
Дополнительный
Недавно я обобщил некоторые знания, связанные с Java, и заинтересованные друзья могут поддерживать их вместе.
Добро пожаловать, чтобы обратить внимание на публичный аккаунт, чтобы общаться вместе: