Кэширование в Java: Map с автоудалением по времени
Быстрый ответ
Для создания самоуничтожающейся карты в Java вы можете воспользоваться кэшем Guava, позволяющим удалять элементы после определённого периода времени. В таком случае нам поможет CacheBuilder
, автоматически указывающий время жизни каждого ключа:
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
Cache<String, String> expiringCache = CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
expiringCache.put("key", "value"); // Время работы – 10 минут, как время на парковке!
Представленный код создаёт карту, элементы из которой исчезают через 10 минут после добавления. Не забудьте включить Guava в зависимости вашего проекта, чтобы использовать CacheBuilder
.
Рассмотрение альтернативных решений
Если же Guava не подходит для ваших задач, существуют альтернативные решения для создания карты с ограничением времени жизни элементов. Однако, для их интегрирования потребуются некоторые компромиссы.
CompletableFuture для очистки
С помощью CompletableFuture
можно назначить удаление данных. Хоть это не является по настоящему функционирующим кэшированием, такой подход подойдёт для выполнения простых задач:
Map<String, Pair<String, CompletableFuture<Void>>> map = new ConcurrentHashMap<>();
map.put("key", Pair.of("value", CompletableFuture.runAsync(() -> {
try {
Thread.sleep(TimeUnit.MINUTES.toMillis(10));
map.remove("key");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}))); // Этот ключ исчезнет быстрее, чем последний кусок пиццы на вечеринке
Такая реализация позволяет запланировать CompletableFuture
для удаления ключа после определённого времени. Это решение эффективно, но не является лучшим выбором для масштабируемых задач.
Потоковый планировщик для очистки
Если требуется более глубокий контроль над процессом, можно применить ScheduledExecutorService
, который будет постоянно удалять устаревшие элементы:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(() -> map.keySet().removeIf(key -> /* логика скорости извлечения */),
0, 10, TimeUnit.MINUTES); // Так данные играют свою последнюю ноту!
Этот метод обеспечивает регулярное удаление данных , что особенно актуально, если алгоритм проверки срока удаления не является тривиальным.
Визуализация
Представьте себе парковку, где места предоставляются автомобилям на ограниченный промежуток времени:
🚗 = Автомобиль (Данные)
⏳ = Время до убытия
🅿️ = Парковочное место (Ключ)
Как только время вышло, машина покидает парковку, освобождая место:
Парковка (первоначально):
| 🅿️ 1 | 🅿️ 2 | 🅿️ 3 |
| 🚗⏳ | 🚗⏳ | 🚗⏳ |
Парковка (некоторое время спустя):
| 🅿️ 1 | 🅿️ 2 | 🅿️ 3 |
| (вакантно)| 🚗⏳ | 🚗⏳ |
Аналогичным образом, карта с установленным временем жизни удаляет ключи после окончания их срока действия:
Кэш (в начале):
| Ключ 1 | Ключ 2 | Ключ 3 |
| Знач⏳ | Знач⏳ | Знач⏳ |
Кэш (когда сроки истекают):
| Ключ 1 | Ключ 2 | Ключ 3 |
| (удалено) | Знач⏳ | Знач⏳ |
Ключи, аналогично парковочным местам, освобождаются по истечении заданного времени.
Другие возможности кэширования
В дополнение к Guava, существует большое количество кэш-библиотек, способных удовлетворить ваши потребности. Кратко остановимся на некоторых из них:
WeakConcurrentHashMap на GitHub
Эта карта идеально подходит для эффективного использования памяти. Она автоматически удаляет элементы, которые более не нужны:
WeakConcurrentHashMap<String, MyObject> map = new WeakConcurrentHashMap<>(timeout, unit);
map.put("key", new MyObject()); // Успешно справляется с очисткой места, как самоочищающийся духовой шкаф
PassiveExpiringMap из Apache Commons
Простое и легковесное решение, однако в ситуации с многопоточностью без дополнительной синхронизации потребуется особая осторожность:
Map<String, String> expiringMap = new PassiveExpiringMap<>(TimeUnit.MINUTES.toMillis(10));
expiringMap.put("key", "value"); // Срок существования ограничен, как у абонемента в спортзал
Ehcache
Мощное решение с множеством функций для корпоративных приложений, поддерживающее настройку в коде:
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
cacheManager.init();
Cache<Long, String> myCache = cacheManager.createCache("myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.heap(100)).withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10))));
// Оснащён большим количеством функций, чем современный смартфон!
Выбирая систему кэширования, следует учитывать такие факторы, как производительность, параллельный доступ, потребление памяти и удобство обслуживания.
Полезные материалы
- CacheBuilder (Guava: Google Core Libraries for Java 23.0 API) — официальная документация по утилитам кэширования от Guava.
- GitHub – ben-manes/caffeine: A high performance caching library for Java — современная и высокопроизводительная библиотека кэширования для Java.
- Documentation — официальная документация фреймворка Ehcache для эффективного кэширования в Java.
- GitHub – jhalterman/expiringmap: A high performance thread-safe map that expires entries — реализация карты Java с автоматическим истечением срока действия записей.
- Reactor 3 Reference Guide — введение в Reactor 3, ключевую библиотеку для реактивного программирования в Java.
- The Java Community Process(SM) Program – JSRs: Java Specification Requests – detail JSR# 107 – детали спецификации JCache (JSR-107), стандартные кэширующие аннотации для Java.