На самом деле то, что вы пишете, является ленивым оценщиком.Вы предоставляете Supplier
для значения, не вычисляя его в первый раз.В тот момент, когда кто-то запрашивает ваше значение, вы вычисляете его и возвращаете его, запоминая (кэшируя) его для будущего использования.
Посмотрите на класс Lazy
Вавра, он делает именно это (но для одного значения)).Вы можете взять некоторые идеи из того, что это делает, а также некоторые дополнительные служебные методы, такие как проверка того, было ли оно уже вычислено.
https://github.com/vavr-io/vavr/blob/master/vavr/src/main/java/io/vavr/Lazy.java
Другой вариант - просто использовать ConcurrentHashMap
.Он предоставляет методы для безопасного (атомарного) обновления значений, если их нет на карте.
Если вы хотите, чтобы они были асинхронными, вам нужно ввести некоторые ExecutorService
или использовать CompletableFuture
(с вашим собственным * 1015).* или пул потоков по умолчанию, который используется параллельными потоками и т. д.).Например:
public class Cache<K,V> implements ICache<K,V> {
Map<K,V> cache = new ConcurrentHashMap<>();
IDataSource<K,V> dataSource;
public Cache(IDataSource<K,V> dataSrc) {
dataSource = dataSrc;
}
// async non-blocking call
public CompletableFuture<V> getAsync(K key) {
return CompletableFuture.supplyAsync(() -> get(key));
}
// blocking call
public V get(K key) {
//computeIfAbsent is atomic and threadsafe, in case multiple CompletableFutures try this in parallel
return cache.computeIfAbsent(key, (k) -> dataSource.get(k));
}
}
Если вы также хотите иметь асинхронное прямое обновление кеша и источника данных, вы можете сделать что-то вроде:
public CompletableFuture<Void> putAsync(K key, V value) {
return CompletableFuture.runAsync(() -> {
synchronized (cache) {
dataSource.put(key, value);
cache.put(key, value);
}
}
}
Хотя, честно говоря, я бы не использовал 2 точки входа для обновленияdataSource
(кеш и источник данных напрямую).Также трудно сделать этот поток полностью безопасным, не имея synchronized
(который блокирует параллельное кэширование, которое полностью исключает параллельное выполнение, даже если ключ другой).