Java非阻塞式缓存实现

时间:2019-06-04 09:01:33

标签: java nonblocking

(请注意,我不能将外部库用于缓存)。

---可以使用流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
    }
}
}

你能建议吗?

您认为它需要更多功能吗?

1 个答案:

答案 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(即使键名不同,也会阻止并发缓存完全并行发生,则很难确保完全线程安全)。