Время от времени последние несколько недель я пытался найти свою идеальную реализацию кеширования, используя MapMaker от guava. См. Мои предыдущие два вопроса здесь и здесь , чтобы следить за моим мыслительным процессом.
Принимая то, что я узнал, моя следующая попытка собирается отказаться от мягких значений в пользу максимального размера и expireAfterAccess:
ConcurrentMap cache = new MapMaker()
.maximumSize(MAXIMUM_SIZE)
.expireAfterAccess(MINUTES_TO_EXPIRY, TimeUnit.MINUTES)
.makeComputingMap(loadFunction);
where
Function loadFunction = new Function() {
@Override
public MyObject apply(String uidKey) {
return getFromDataBase(uidKey);
}
};
Однако одна остающаяся проблема, с которой я все еще борюсь, заключается в том, что эта реализация будет вытеснять объекты, даже если они сильно достижимы, когда их время истекло. Это может привести к тому, что в среде будут перемещаться несколько объектов с одинаковым UID, чего я не хочу (я считаю, что то, что я пытаюсь достичь, известно как канонизация).
Итак, насколько я могу судить, единственный ответ - иметь дополнительную карту, которая функционирует как интернер, которую я могу проверить, чтобы увидеть, находится ли объект данных все еще в памяти:
ConcurrentMap interner = new MapMaker()
.weakValues()
.makeMap();
, и функция загрузки будет пересмотрена:
Function loadFunction = new Function() {
@Override
public MyObject apply(String uidKey) {
MyObject dataObject = interner.get(uidKey);
if (dataObject == null) {
dataObject = getFromDataBase(uidKey);
interner.put(uidKey, dataObject);
}
return dataObject;
}
};
Однако использование двух карт вместо одной для кеш кажется неэффективным. Есть ли более изощренный подход к этому? В общем, правильно ли я поступаю, или мне следует переосмыслить свою стратегию кэширования?