guava 三日谈

平常工作中用到guava的地方越来越多,大多的采用的是常规官方demo的simple Samples用法,业务使用的地方多了,这种埋点模式需要进一步升级了,升级的过程中,各种新鲜事物出来了,不明白不了解的地方也多了,那就花3天时间研究下guava框架,看看还有没有什么好东西,收好顺便再吐回到产品中去。

先看下guava下的包结构

图片

因为双十一项目大版本上线,两个通宵对30岁的人来说已经不比当年了,对guava的研究一拖再拖,工作为重,不以学以致用的学习都是浪费脑空间了,guava小小工具类里面有太多的好东西,不好好学习下太可惜了,平常能够使用到的比如对象静态构建方法、cache使用、异步和并发是很多开源框架的最爱,但是这里的很多功能无一不是对java api的补充和升级,在使用guava的同时如果了解内部的这种补充也是对java底层实现的最好学习方式,所以,还是每天来一点吧。

day1 cache

cache是平常用到的最常见的工具,在平常使用中可被替代性较低。

先看代码: https://github.com/xuminwlt/j360-jdk/tree/master/j360-jdk-guava

Guava Cache有两种创建方式:

  1. cacheLoader
  2. callable callback

Cache回收的参数:

  1. 大小的设置:CacheBuilder.maximumSize(long) CacheBuilder.weigher(Weigher) CacheBuilder.maxumumWeigher(long)
  2. 时间:expireAfterAccess(long, TimeUnit) expireAfterWrite(long, TimeUnit)
  3. 引用:CacheBuilder.weakKeys() CacheBuilder.weakValues() CacheBuilder.softValues()
  4. 明确的删除:invalidate(key) invalidateAll(keys) invalidateAll()
  5. 删除监听器:CacheBuilder.removalListener(RemovalListener)

refresh机制:

  1. LoadingCache.refresh(K) 在生成新的value的时候,旧的value依然会被使用。
  2. CacheLoader.reload(K, V) 生成新的value过程中允许使用旧的value
  3. CacheBuilder.refreshAfterWrite(long, TimeUnit) 自动刷新cache

使用:

cache在平常使用中可以被作用两种用途

  1. 一种WeakHashMap的并发安全版
  2. 可实现定时更新的cache

第一种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//定义一个普通的Guava Cache,不需要用到load方法
public static <K,V> LoadingCache<K,V> cache() {
LoadingCache<K, V> cache = (LoadingCache<K, V>) CacheBuilder.newBuilder()
.expireAfterAccess(30, TimeUnit.SECONDS)
.maximumSize(100)
//设置缓存容器的初始容量为10
.initialCapacity(7)
.refreshAfterWrite(1, TimeUnit.SECONDS)
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(16)
.removalListener(new RemovalListener<K, V>() {
@Override
public void onRemoval(RemovalNotification<K, V> rn) {
System.out.println(rn.getKey() + "被移除");
}
})
.build();
return cache;
}

第二种在使用中是真正作为Cache在使用的场景,但是这里定时更新需要注意的点是默认情况下的cache在reload时的同步机制,也就是会产生阻塞行为,所以在这里需要定义一个异步执行的Cache工具类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class CacheLoaderImpl extends CacheLoader {
private JdbcTemplate jdbcTemplate;
public CacheLoaderImpl(JdbcTemplate jdbcTemplate){
this.jdbcTemplate = jdbcTemplate;
}
public static <K, V> CacheLoader<K, V> asyncReload(final CacheLoader<K, V> loader,
final Executor executor) {
checkNotNull(loader);
checkNotNull(executor);
return new CacheLoader<K, V>() {
@Override
public V load(K key) throws Exception {
return loader.load(key);
}
@Override
public ListenableFuture<V> reload(final K key, final V oldValue) throws Exception {
ListenableFutureTask<V> task = ListenableFutureTask.create(new Callable<V>() {
@Override
public V call() throws Exception {
System.out.println("async impl load");
return loader.reload(key, oldValue).get();
}
});
executor.execute(task);
return task;
}
@Override
public Map<K, V> loadAll(Iterable<? extends K> keys) throws Exception {
return loader.loadAll(keys);
}
};
}
@Override
public Object load(Object o) throws Exception {
System.out.println("impl load");
return jdbcTemplate.getDefaultKey(String.valueOf(o));
}
}

调用时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//实现自动刷新的cache
public static <K,V> LoadingCache<K,V> asyncCache(JdbcTemplate jdbcTemplate){
//core=1+无界队列的线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 10,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
CacheLoader<K, V> loader = CacheLoaderImpl.asyncReload(new CacheLoaderImpl(jdbcTemplate), pool);
//
return (LoadingCache<K, V>) CacheBuilder.newBuilder().expireAfterAccess(30, TimeUnit.SECONDS)
.maximumSize(100)
//设置缓存容器的初始容量为10
.initialCapacity(7)
.refreshAfterWrite(1, TimeUnit.SECONDS)
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(16)
.removalListener(new RemovalListener<K, V>() {
@Override
public void onRemoval(RemovalNotification<K, V> rn) {
System.out.println(rn.getKey() + "被移除");
}
})
.build(loader);
}

day2 Util、Functional idioms、Concurrency、Strings

Guava提供了很多基础的工具类,比如Optional、Ordering、PreCondition、Throwables等,分别作为jdk的功能补充,但是随着现在jdk8的新项目大量使用,jdk8自身的很多功能可以逐渐替换掉Guava的部分功能类,所以把精力逐渐更多放在jdk8上面,后面随着新项目对jdk8的这些功能再做总结。

并发框架中的 ListenableFuture和Callback是许多异步实现的最爱,因为对jdk8的completeFuture还没有深入研究,不是很清楚是否可被替换,待深入再做总结。

day3 Collections、Ranges、EventBus

Collections是Guava另一个很赞的工具类。常用的不可变集合是一些非常不错的类,另外还有jdk的类的guava式创建方式也是非常漂亮的代码结构。

Graphs、Net、Io

guava20.0版本新增了图的类,用于存储和分析node and edge的数据信息,在基于关系的一些概念中应该有不错的使用场景,net和io里面的内容较少使用,不做记录。

Guava Math