Caches
示例
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
适用性
缓存在多种场景下都是十分有用的。例如,当值的计算或检索代价很高时,并且您需要多次在某个输入上使用它的值,您应该考虑使用缓存。
Cache
类似于 ConcurrentMap
,但并不完全相同。最根本的区别在于 ConcurrentMap
会持久保存添加到其中的所有元素,直到它们被明确移除。另一方面,缓存通常被配置为自动移除元素,以便限制其内存占用。在某些情况下,即使它没有移除元素,由于自动缓存加载的特性, LoadingCache
也变得很有用。
通常,Guava 缓存工具程序适用于以下情况:
- 你愿意花些内存来提高速度。
- 你希望有时会多次查询键值。
- 你需要缓存数据的要小于 RAM 的容量。 (Guava 缓存是一个单独运行的应用程序的本地缓存。它们不会将数据存储在文件中或外部服务器上。如果这不符合你的需求,请考虑像 Memcached 这样的工具。)
如果以上几点都适用于您的应用,那么 Guava 缓存工具程序将很适合你!
如上面的示例代码所示,你可以使用 CacheBuilder
构建器模式获取 Cache
对象,但自定义 Cache 也是很有趣的部分。
注意:如果您不需要 Cache
的特性,ConcurrentHashMap
的内存效率会更高 - 但是使用任何旧的 ConcurrentMap
复制大多数 Cache
的功能是极其困难或不可能的。
Population
在设计自己的缓存时要考虑的第一个问题是:是否有一些合理的默认函数来加载或计算与 key 相关的值? 如果是这样,您应该使用 CacheLoader
。 如果没有,或者你需要覆盖默认值,但仍然需要原子 “get-if-absent-compute” 语义,则应该将 Callable
传递给 get
调用。 可以使用 Cache.put
直接插入元素,但首选自动缓存加载,因为它可以更容易地推断所有缓存内容的一致性。
From a CacheLoader
LoadingCache
是使用附加的 CacheLoader
构建的 Cache
。 创建 CacheLoader
通常与实现 V load(K key) throws Exception
方法一样简单。 因此,例如,您可以使用以下代码创建 LoadingCache
:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
...
try {
return graphs.get(key);
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
查询 LoadingCache
的标准方法是使用方法 get(K)
。 这将返回已缓存的值,或者使用 Cache
的 CacheLoader
以原子方式将新值加载到缓存中。 因为 CacheLoader
可能抛出异常,所以 LoadingCache.get(K)
方法签名要抛出 ExecutionException
。(如果缓存加载器抛出未检异常, get(K)
将抛出一个包含它的 UncheckedExecutionException
。)你还可以选择使用 getUnchecked(K)
,它包装 UncheckedExecutionException
中的所有异常,但如果底层CacheLoader 正常抛出受检异常,这可能会导致奇怪的行为。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return createExpensiveGraph(key);
}
});
...
return graphs.getUnchecked(key);
可以使用方法 getAll(Iterable <?extends K>)
执行批量查找。 默认情况下,getAll
将为缓存中不存在的每个密钥发出对 CacheLoader.load
的单独调用。 当批量检索比许多单独查找更有效时,您可以覆盖 CacheLoader.loadAll
来扩展它。 getAll(Iterable)
的性能将相应提高。
请注意,您可以编写一个 CacheLoader.loadAll
实现来加载未特别请求的键的值。 例如,如果计算某个组中任何键的值会为您提供组中所有键的值,则 loadAll
可能会同时加载该组的其余部分。