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