GUAVA--缓存(概述)

一、适用性

缓存在不少场景下都是至关有用的。例如,计算或检索一个值的代价很高,而且对一样的输入须要不止一次获取 值的时候,就应当考虑使用缓存。java

Guava Cache 与 ConcurrentMap 很类似,但也不彻底同样。最基本的区别是 ConcurrentMap 会一直保存所 有添加的元素,直到显式地移除。相对地,Guava Cache 为了限制内存占用,一般都设定为自动回收元素。在 某些场景下,尽管 LoadingCache 不回收元素,它也是颇有用的,由于它会自动加载缓存。缓存

一般来讲,Guava Cache 适用于:服务器

  • 你愿意消耗一些内存空间来提高速度。
  • 你预料到某些键会被查询一次以上。
  • 缓存中存放的数据总量不会超出内存容量。(Guava Cache 是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。若是这不符合你的需求,请尝试 Memcached 这类工具)

若是你的场景符合上述的每一条,Guava Cache 就适合你。ide

如同范例代码展现的同样,Cache 实例经过 CacheBuilder 生成器模式获取,可是自定义你的缓存才是最有趣的部分。工具

注:若是你不须要 Cache 中的特性,使用 ConcurrentHashMap 有更好的内存效率——但 Cache 的大多数特性都很难基于旧有的 ConcurrentMap 复制,甚至根本不可能作到。性能

二、加载

在使用缓存前,首先问本身一个问题:有没有合理的默认方法来加载或计算与键关联的值?若是有的话,你应当使用 CacheLoader。若是没有,或者你想要覆盖默认的加载运算,同时保留"获取缓存-若是没有-则计算"[get-if-absentcompute]的原子语义,你应该在调用 get 时传入一个 Callable 实例。缓存元素也能够经过 Cache.put 方法直接插入,但自动加载是首选的,由于它能够更容易地推断全部缓存内容的一致性。ui

2.一、CacheLoader

LoadingCache 是附带 CacheLoader 构建而成的缓存实现。建立本身的 CacheLoader 一般只须要简单地实 现 V load(K key) throws Exception 方法。例如,你能够用下面的代码构建 LoadingCache:code

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)方法。这个方法要么返回已经缓存的值,要么使用 CacheLoader 向缓存原子地加载新值。因为 CacheLoader 可能抛出异常,LoadingCache.get(K)也声明为抛出 ExecutionException 异常。若是你定义的CacheLoader 没有声明任何检查型异常,则能够经过 getUnchecked(K)查找缓存;但必须注意,一旦 CacheLoader 声明了检查型异常,就不能够调用 getUnchecked(K)。内存

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES)
		.build(new CacheLoader<Key, Graph>() {
			public Graph load(Key key) { // 没有检查异常
				return createExpensiveGraph(key);
			}
		});
		...
return graphs.getUnchecked(key);

getAll(Iterable<? extends K>)方法用来执行批量查询。默认状况下,对每一个不在缓存中的键,getAll 方法会单独调用 CacheLoader.load 来加载缓存项。若是批量的加载比多个单独加载更高效,你能够重载 CacheLoader.loadAll 来利用这一点。getAll(Iterable)的性能也会相应提高。ci

注:CacheLoader.loadAll 的实现能够为没有明确请求的键加载缓存值。例如,为某组中的任意键计算值时,可以获取该组中的全部键值,loadAll 方法就能够实现为在同一时间获取该组的其余键值。校注:getAll(Iterable<?extends K>)方法会调用 loadAll,但会筛选结果,只会返回请求的键值对。

2.二、Callable

全部类型的 Guava Cache,无论有没有自动加载功能,都支持 get(K, Callable)方法。这个方法返回缓存中相应的值,或者用给定的 Callable 运算并把结果加入到缓存中。在整个加载方法完成前,缓存项相关的可观察状态都不会更改。这个方法简便地实现了模式"若是有缓存则返回;不然运算、缓存、而后返回"。

Cache<Key, Graph> cache = CacheBuilder.newBuilder().maximumSize(1000).build();
		...
try {
	// 若是密钥不在
	cache.get(key, new Callable<Key, Graph>() {
		@Override
		public Value call() throws AnyException {
			return doThingsTheHardWay(key);
		}
	});
} catch (ExecutionException e) {
	throw new OtherException(e.getCause());
}

三、显式插入

使用 cache.put(key, value)方法能够直接向缓存中插入值,这会直接覆盖掉给定键以前映射的值。使用 Cache.asMap()视图提供的任何方法也能修改缓存。但请注意,asMap 视图的任何方法都不能保证缓存项被原子地加载到缓存中。进一步说,asMap 视图的原子运算在 Guava Cache 的原子加载范畴以外,因此相比于 Cache.asMap().putIfAbsent(K, V),Cache.get(K, Callable) 应该老是优先使用。

四、简单例子

4.一、建立cache

LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(new CacheLoader<Object, Object>() { 
	// 若没有元素,则建立而且放入缓存
	@Override
	public Object load(Object key) throws Exception {
		return key.hashCode();
	}
});

4.二、初始化大小,个数设置

LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().initialCapacity(10)
		// 初始化个数
		.maximumSize(55)// 设置最大个数
		.build(new CacheLoader<Object, Object>() { // 若没有元素,则建立而且放入缓存
			@Override
			public Object load(Object key) throws Exception {
				return key.hashCode();
			}
		});

4.三、重量设置

LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().maximumWeight(1000) 
		// 设置重量,配合weigher使用
		.weigher(new Weigher<Object, Object>() {
			@Override
			public int weigh(Object key, Object value) {
				return 100;
			}
		}).build(new CacheLoader<Object, Object>() { // 若没有元素,则建立而且放入缓存
			@Override
			public Object load(Object key) throws Exception {
				return key.hashCode();
			}
		});
相关文章
相关标签/搜索