Guava Cache异步刷新的一个实现
Guava的cache提供了refresh功能。 在指定的时间间隔后,guava可以(惰性地/lazily)更新缓存。 默认的refresh实现是同步的(synchronously),一个线程在更新缓存时,其它线程会等待。 具体见LoadingCache和CacheLoader的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
在一篇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.exp
和p2.exp
的比较有结果时(不等于0),就不会再去比较age
。