Guava Cache异步刷新的一个实现

2018 Jan 16

Guava的cache提供了refresh功能。 在指定的时间间隔后,guava可以(惰性地/lazily)更新缓存。 默认的refresh实现是同步的(synchronously),一个线程在更新缓存时,其它线程会等待。 具体见LoadingCacheCacheLoader的javadoc。

LoadingCache
void refresh(K key)
… Loading is asynchronous only if CacheLoader.reload(K, V) was overridden with an asynchronous implementation.

CacheLoader
public ListenableFuture<V> reload(K key, V oldValue) throws Exception
… This implementation synchronously delegates to load(K).

下面提供了一个异步的CacheLoader实现,使得一个线程在refresh缓存时,其它线程可以不必等待,继续使用旧的缓存值。

Guava中的Utility Object Pattern

2016 Dec 20

在一篇Guava的presentation中,Google提到了一种叫“Utility Object pattern”的模式。

return Joiner.on(", ").skipNulls().join("one", null, "two", "three");
             |-- 1 ---|-- 2 ------|-- 3 ----------------------------|

return Splitter.on("|").omitEmptyStrings().split("|Harry||Ron|||Hermione  ||");
               |-- 1 --|-- 2 -------------|-- 3 ------------------------------|

通过1得到一个初始的、默认的utility对象,通过2可以得到utility对象的一个“变种”, 再通过3最后求值。

这种API看起来更简洁。用户不用知道不同变种对应的utility类。

实现上,2中的方法,比如skipNulls(),会返回一个新对象,或者修改对象的属性并返回它。

  public Joiner skipNulls() {
    return new Joiner(this) {//通过传入的this可以“继承”上一个Utility对象的某些特性
      @Override
      public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
      //...
      }
      //...
    }
  }
  public Splitter omitEmptyStrings() {
    //直接构造相应的Utility对象
    return new Splitter(strategy, true, trimmer, limit);
  }

ComparisonChain是另一个有趣的例子。

Comparator<Person> comparator = new Comparator<Person>() {
  public int compare(Person p1, Person p2) { 
    return ComparisonChain.start().compare(p1.exp, p2.exp).compare(p1.age, p2.age).result();
  }
};  

使用ComparisonChain时,不会创建新的ComparisonChain对象。 ComparisonChain也支持“short circuit(短路)”。 当p1.expp2.exp的比较有结果时(不等于0),就不会再去比较age