前言
在本地缓存中,最常用的就是OSCache和谷歌的Guava Cache。其中OSCache在07年就停止维护了,但它仍然被广泛的使用。谷歌的Guava Cache也是一个非常优秀的本地缓存,使用起来非常灵活,功能也十分强大,可以说是当前本地缓存中最优秀的缓存框架之一。之前我们分析了OSCache的部分源码,本篇就通过Guava Cache的部分源码,来分析一下Guava Cache的实现原理。
在分析之前,先弄清数据结构的使用。之前的文章提到,OSCache使用了一个扩展的HashTable,作为缓存的数据结构,由于在get操作上,没有使用同步的方式,通过引入一个更新状态数据结构,来控制并发访问的安全。Guava Cache也是使用一个扩展的HashTable作为其缓存数据结构,然而,在实现上,和OSCache是完全不同的。
Guava Cache所用的HashTable和ConcurrentHashMap十分相似,通过引入一个Segment数组,对HashTable进行分段,通过分离锁、final以及volatile的配合,实现了并发环境下的线程安全,同时,性能也非常高(每个Segment段的操作互不影响,即使写操作,只要在不同的Segment上,也完全可以并发的执行)。具体的原理,可以参考ConcurrentHashMap的实现,这里就不进行具体的剖析了。
数据结构核心部分可以通过下面的图形表示:
CacheBuilder
Guava Cache特性
CacheBuilder集成了创建缓存所需的各种参数。正如官方文档介绍的:CacheBuilder将创建一个LoadingCache和Cache的实例,该实例可以包含下面任何特性
- 自动将内容加载到缓存中
- LRU淘汰策略
- 根据上一次访问时间或写入时间决定缓存过期
- key关键字可以采用弱引用(WeakReference)
- value值可以采用弱引用(WeakReference)以及软引用(SoftReference)
- 缓存移除或回收进行通知
- 统计缓存访问性能信息
所有特性都是可选的,创建的缓存可以包含上面所有的特性,也可以都不使用,具有很强的灵活性。
构造示例
下面是一个简单的使用例子:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});}
还可以这样写:
String spec = "maximumSize=10000,expireAfterWrite=10m";
LoadingCache<Key, Graph> graphs = CacheBuilder.from(spec)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});}
说明:上面的例子指定Cache容量最大为10000,并且写入后经过10分钟自动过期,并指定了一个缓存移除的消息监听器,可以在缓存移除的时候,进行指定的操作。
接下来,根据CacheBuilder的源码进行简要的分析:
CacheBuilder中一些重要的参数
//默认容量
private static final int DEFAULT_INITIAL_CAPACITY = 16;
//默认并发程度(segement大小就是通过这个计算)
private static final int DEFAULT_CONCURRENCY_LEVEL = 4;
//默认失效时间
private static final int DEFAULT_EXPIRATION_NANOS = 0;
//默认刷新时间
private static final int DEFAULT_REFRESH_NANOS = 0;
//默认性能计数器
static final Supplier<? extends StatsCounter> NULL_STATS_COUNTER = Suppliers.ofInstance(
new StatsCounter() {
@Override
public void recordHits(int count) {}
@Override
public void recordMisses(int count) {}
@Override
public void recordLoadSuccess(long loadTime) {}
@Override
public void recordLoadException(long loadTime) {}
@Override
public void recordEviction() {}
@Override
public CacheStats snapshot() {
return EMPTY_STATS;
}
});
static final CacheStats EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0);
static final Supplier<StatsCounter> CACHE_STATS_COUNTER =
new Supplier<StatsCounter>() {
@Override
public StatsCounter get() {
return new SimpleStatsCounter();
}
};
//移除事件监听器(默认为空)
enum NullListener implements RemovalListener<Object, Object> {
INSTANCE;
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {}
}
enum OneWeigher implements Weigher<Object, Object> {
INSTANCE;
@Override
public int weigh(Object key, Object value) {
return 1;
}
}
static final Ticker NULL_TICKER = new Ticker() {
@Override
public long read() {
return 0;
}
};
static final int UNSET_INT = -1;
boolean strictParsing = true;
//初始容量
int initialCapacity = UNSET_INT;
//并发程度,