guavaCache学习

2016年01月27日

guava cache

个人yy

guavaCache 如何实现的缓存? 内部持有一个Map?

如何实现 定时刷新 ? 有定时任务定时清理缓存中的对象?

segment的作用

weight 的作用 如果设置了,权重,那么它会影响初始化的大小,如果有个权重为5的对象,那它就相当于有5个权重为1的对象

int initialCapacity = Math.min(builder.getInitialCapacity(), MAXIMUM_CAPACITY);
if (evictsBySize() && !customWeigher()) {
  initialCapacity = Math.min(initialCapacity, (int) maxWeight);
}

concurrencyLevel 影响分段个数

int segmentShift = 0;
int segmentCount = 1;
while (segmentCount < concurrencyLevel
       && (!evictsBySize() || segmentCount * 20 <= maxWeight)) {
  ++segmentShift;
  segmentCount <<= 1;
}
this.segmentShift = 32 - segmentShift;
segmentMask = segmentCount - 1;

this.segments = newSegmentArray(segmentCount);

int segmentCapacity = initialCapacity / segmentCount;
if (segmentCapacity * segmentCount < initialCapacity) {
  ++segmentCapacity;
}

int segmentSize = 1;
while (segmentSize < segmentCapacity) {
  segmentSize <<= 1;
}

tricker : 计时器

网上资料

网上关于guavaCache的使用的文章蛮多,但是讲如何实现的并不多,关于guava cache如何做的, 找到这篇好文 Java Cache系列之Guava Cache实现详解 ,这篇文章介绍了,guava cache 的数据结构,接口设计,以及代码实现逻辑,讲的挺好的(此处应竖起大拇指)

源码学习

看完了上面那篇文章后,对Guava Cache有了一定的了解,下面就到了自己看源码,解答自己的疑问,梳理实现逻辑的时候了.

初次见面

既然Guava Cache 是由 CacheBuilder来配置以及实例化的,那自然要先看CacheBuilder咯.

进入 CacheBuilder的源码,首先进入视野的就是CacheBuilder类上的一大片注释说明:

/**
* <p>A builder of {@link LoadingCache} and {@link Cache} instances having any combination of the
* following features:
*
* <ul>
* <li>automatic loading of entries into the cache
* <li>least-recently-used eviction when a maximum size is exceeded
* <li>time-based expiration of entries, measured since last access or last write
* <li>keys automatically wrapped in {@linkplain WeakReference weak} references
* <li>values automatically wrapped in {@linkplain WeakReference weak} or
*     {@linkplain SoftReference soft} references
* <li>notification of evicted (or otherwise removed) entries
* <li>accumulation of cache access statistics
* </ul>
*
... 把说明都看了一遍, 主要是介绍了这个Builder构建的Cache含有的 特性,简单的使用例子,以及特性使用说明.

初步认识

看完注释,往下面走,可以看到CacheBuilder里面 定义的一些属性以及它的默认值(初始化大小,并发等级,计数器,对象移除监听器等)

private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final int DEFAULT_CONCURRENCY_LEVEL = 4;
private static final int DEFAULT_EXPIRATION_NANOS = 0;
private static final int DEFAULT_REFRESH_NANOS = 0;
...
int initialCapacity = UNSET_INT;
int concurrencyLevel = UNSET_INT;
long maximumSize = UNSET_INT;
long maximumWeight = UNSET_INT;
Weigher<? super K, ? super V> weigher;
Strength keyStrength;
Strength valueStrength;
long expireAfterWriteNanos = UNSET_INT;
long expireAfterAccessNanos = UNSET_INT;
long refreshNanos = UNSET_INT;
Equivalence<Object> keyEquivalence;
Equivalence<Object> valueEquivalence;
...

再往下走,可以看到是一些设置Cache特性的一些方法(设置大小,value对象比较器,key比较器,并发等级,权重设置器, 引用类型(软引用,弱引用,强引用), 刷新时间, 过期时间, 对象移除监听器等), 这里需要注意一点的是,所有的这些 属性设置方法中,都有对属性是否被设置过的判断,如果以及被设置过了,再次设置的时候,会抛出异常,也就是说, 这些属性是不能被重复设置的.

public CacheBuilder<K, V> initialCapacity(int initialCapacity)
public CacheBuilder<K, V> concurrencyLevel(int concurrencyLevel)
public CacheBuilder<K, V> weakKeys()
public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit)
public CacheBuilder<K, V> refreshAfterWrite(long duration, TimeUnit unit)
...

drainReferenceQueues() 中为何最多排除 16个entry