caffeine: Java 8高性能缓存库包

17-04-06 banq
Caffeine提供一个in-memory缓存,类似Google Guava的API,在Guava基础上提高了各种体验。

通常我们对缓存有两种操作,存入缓存和更新缓存:

Cache<K, V> cache = ...

public V read(K key) {
  V result = cache.getIfPresent(key);
  if (result == null) {
    result = readFromDatabase(key);
    cache.put(key, result);
  }

  return result;
}

public void write(K key, V value) {
  writeToDatabase(key, value);
  // force the value to be re-read from 
  // the database on next read(key) invocation
  cache.invalidate(key); 
};
<p>

这是一种get-if absent compute-put模式,也就是如果缓存没有该元素,则从数据库获得放入缓存。

假设有两个线程T1和T2,第一个调用缓存读取,第二个缓存写入,两个线程同时运行时,缓存中获取的值可能不是刚刚写入数据库值,也就是脏数据。

使用Guava测试代码如下:

AtomicReference<Integer> db = new AtomicReference<>(1);

LoadingCache<String, Integer> c = CacheBuilder.newBuilder().build(
    new CacheLoader<String, Integer>() {
        public Integer load(String key) {
            println("Reading from db ...");
            Integer v = db.get();
            sleep(1000L);
            println("Read from db: " + v);
            return v;
        }
    }
);

Thread t1 = new Thread(() -> {
    Integer g = c.get("k");
    println("Got from cache: " + g);
});

Thread t2 = new Thread(() -> {
    sleep(500L);
    println("Writing to db ...");
    db.set(2);
    println("Wrote to db");
    c.invalidate("k");
    println("Invalidated cached");
});

t1.start();
t2.start();

t1.join();
t2.join();

System.out.println();
println("In cache: " + c.get("k"));
<p>

得到结果:

Thread-1 (123): Reading from db ...
Thread-2 (612): Writing to db ...
Thread-2 (612): Wrote to db
Thread-2 (613): Invalidated cached
Thread-1 (1142): Read from db: 1
Thread-1 (1145): Got from cache: 1

main (1146): In cache: 1

尽管缓存已经invalidate,但是缓存还是能获得K的值。

而如果使用Caffeine.newBuilder(),能得到正确结果:

Thread-1 (122): Reading from db ...
Thread-2 (624): Writing to db ...
Thread-2 (624): Wrote to db
Thread-1 (1146): Read from db: 1
Thread-2 (1146): Invalidated cached
Thread-1 (1146): Got from cache: 1

main (1147): Reading from db ...
main (2149): Read from db: 2
main (2149): In cache: 2
<p>

这获得了正确及俄国,是因为Caffeine 有一个 key-scoped锁,当调用cache.get时,指定键key的锁会一直保留到其值被计算加载完成。

GitHub - ben-manes/caffeine: A high performance ca

    

1
猜你喜欢