缓存在很多场景下都是相当有用的,比如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。开发中也有很多可以选择,比如EhCache、Redis等都是不错的解决方案。作为一个比较全面的开发工具包,Guava也提供了一套缓存工具,不过我看了一下,感觉其实使用起来并没有Redis方便(EhCache还没使用过)。相关文档也表示,不建议使用Guava做大规模缓存工具。所以本片文章,我只简单的做一个Guava Cache使用的例子,不做太多深入的学习,有兴趣的同学可以去深入了解一下,Guava Cache用户文档
通常来说,Guava Cache适用于:
你愿意消耗一些内存空间来提升速度。
你预料到某些键会被查询一次以上。
缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具)
如果你的场景符合上述的每一条,Guava Cache就适合你。
如同范例代码展示的一样,Cache实例通过CacheBuilder生成器模式获取,但是自定义你的缓存才是最有趣的部分。
注:如果你不需要Cache中的特性,使用ConcurrentHashMap有更好的内存效率——但Cache的大多数特性都很难基于旧有的ConcurrentMap复制,甚至根本不可能做到。
任何cache都应该提供get-if-absent-compute这一基础原子语义,具体含义如下:
- 从缓存中取。
- 缓存中存在该数据,直接返回;
- 缓存中不存在该数据,从数据源中取。
- 数据源中存在该数据,放入缓存,并返回;
- 数据源中不存在该数据,返回空。
如下例子使用Guava Cache实现一个DAO层的缓存,getList对外提供获取List列表方法并整合Guava Cache实现缓存,getListFromDb方法模拟从数据库获取数据,符合上述get-if-absent-compute原子语义。
public class Dao {
private Cache<String, List<String>> poiCache = CacheBuilder.newBuilder().build();
@SuppressWarnings("unchecked")
public List<String> getList(final String cityId) {
List returnList = null;
try {
returnList = poiCache.get(cityId, () -> getListFromDb(cityId));
} catch (ExecutionException e) {
// 记日志
}
return returnList;
}
@SuppressWarnings("unchecked")
private List<String> getListFromDb(String cityId){
System.out.println("getting from DB, please wait...");
List<String> returnList = Lists.newArrayList();
// 模拟从数据库中取数据
try {
Thread.sleep(1000);
switch (cityId){
case "0101" :
returnList = ImmutableList.of("北京", "上海", "广州", "深圳"); break;
case "0102" :
returnList = ImmutableList.of("a", "b", "c", "d"); break;
}
} catch (Exception e) {
// 记日志
}
return returnList;
}
}
测试缓存是否生效:
public class CacheTest {
public static void main(String[] args) {
Dao dao = new Dao();
for (int i = 0; i < 3; i++) {
System.out.println("--- " + i + " ---");
System.out.println(dao.getList("0101"));
System.out.println(dao.getList("0102"));
System.out.println(dao.getList("0103"));
System.out.println(dao.getList("0104"));
System.out.println();
}
}
}
结果输出:
Connected to the target VM, address: '127.0.0.1:5298', transport: 'socket'
--- 0 ---
getting from DB, please wait...
[北京, 上海, 广州, 深圳]
getting from DB, please wait...
[a, b, c, d]
getting from DB, please wait...
[]
getting from DB, please wait...
[]
--- 1 ---
[北京, 上海, 广州, 深圳]
[a, b, c, d]
[]
[]
--- 2 ---
[北京, 上海, 广州, 深圳]
[a, b, c, d]
[]
[]
只有第一次查询调用了getListFromDb方法,说明缓存生效了。分享一篇我感觉写的比较好的关于Guava Cache的文章,有兴趣的同学可以看一下Guava Cache内存缓存使用实践-定时异步刷新及简单抽象封装