(请注意,我不能将外部库用于缓存)。
---可以使用流API来完成吗? ---
我需要实现一个缓存,它具有1个关键属性:
如果要求高速缓存提供它不包含的键,则应使用外部提供的功能从另一个源(数据库或类似资源)读取数据来获取数据。
我已经开始创建基本的骨架代码:
public interface ICache<K,V> {
}
interface IDataSource<K,V> {
void put(K key, V value);
V get(K key);
}
public class Cache<K,V> implements ICache<K,V> {
Map<K,V> cache = new HashMap<>();
IDataSource<K,V> dataSource;
public Cache(IDataSource<K,V> dataSrc) {
dataSource = dataSrc;
}
//may it change to a future? how it can be done?
public V getAsync(K key) {
if (cache.containsKey(key)) {
return cache.get(key);
}
else {
//do some async op
}
}
}
你能建议吗?
您认为它需要更多功能吗?
答案 0 :(得分:2)
实际上,您所写的只是一个懒惰的评估者。您正在为值提供Supplier
,而不是第一次进行计算。当有人要求您提供您的值时,您便会对其进行计算并返回,并记住(缓存)以备将来使用。
看看Vavr的Lazy
类,它就是这样做的(但是对于单个值)。您可以从正在做的事情中获取一些想法,以及一些额外的实用程序方法,例如检查它是否已经计算。
https://github.com/vavr-io/vavr/blob/master/vavr/src/main/java/io/vavr/Lazy.java
另一种选择是仅使用ConcurrentHashMap
。如果值不在地图中,它会提供安全(原子地)更新值的方法。
如果您希望它是异步的,则需要引入一些ExecutorService
或使用CompletableFuture
(与您自己的ExecutorService
或并行流等使用的默认线程池一起使用)。 。例如:
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
(直接缓存和dataSource)。同样,如果没有synchronized
(即使键名不同,也会阻止并发缓存完全并行发生,则很难确保完全线程安全)。