建立緩存實體LoadingCache
LoadingCache只是個接口,具體的還是LocalLoadingCache,其實真正管理緩存的是LocalCache。
class LocalLoadingCache<K, V> extends LocalManualCache<K, V> implements LoadingCache<K, V> {
LocalLoadingCache(CacheBuilder<? super K, ? super V> builder, CacheLoader<? super K, V> loader){
super(new LocalCache<K, V>(builder, checkNotNull(loader)));
}
其餘的都交給了LocalCache
}
先來看下各個字段所代表的含義
CacheBuilder.newBuilder()
.concurrencyLevel(8) //根據這個來計算分成幾個緩存區
.expireAfterWrite(8, TimeUnit.SECONDS)//設置寫緩存後8秒鐘過期
.initialCapacity(10)//設置緩存容器的初始容量爲10
.maximumSize(100) //設置緩存最大容量爲100,超過100之後就會按照LRU最近雖少使用算法來移除緩存項
...
以下的maxWeight 就是指maximumSize
1. 先看LocalCache的Segment[] 數組; 緩存區
分成幾段緩存區segment,獲取的時候根據key的hashcode值計算來定位到segment .這主要是爲了查詢效率
在LocalCache的構造器中創建好了segment數組.
1 ) 創建segment數組. 先根據concurrencyLevel 來計算出數組的大小,默認是4.
int segmentShift = 0;
int segmentCount = 1;
//個數< concurrencyLevel & (沒有設置回收超出數 || 個數*20 <= 回收超出數)
while (segmentCount < concurrencyLevel && (maxWeight <0 || segmentCount * 20 <= maxWeight)) {
++segmentShift;
segmentCount <<= 1; //相當於segmentCount = segmentCount*2
}
//創建好總共分爲幾個hash段
this.segments = new Segment[segmentCount];
2 ) 根據初始化容量initialCapacity,爲每個緩存區分配初始化緩存數
根據initialCapacity(默認是16,這個是緩存初始化個數)和緩存區的個數(segment數組的個數)來計算出平均每個segment裏的緩存數
int segmentSize = 1;
int segmentCapacity = initialCapacity / segmentCount;
if (segmentCapacity * segmentCount < initialCapacity) {
++segmentCapacity;
}
while (segmentSize < segmentCapacity) {
segmentSize <<= 1;
}
3 ) 計算每個緩存區中最大的緩存數]
根據緩存的最大容量和緩存區數,爲每個緩存區分配合理的緩存個數
private List<Integer> getSegmentNum() {
List<Integer> arr = new ArrayList<Integer>();
long maxSegmentWeight = maxWeight / segmentCount + 1;
long remainder = maxWeight % segmentCount;
for (int i = 0; i < this.segments.length; ++i) {
//限制緩存數時,爲每個hash段內的限制數分配下
if(maxWeight >=0 ) {
if (i == remainder) {
maxSegmentWeight--;
arr.add(maxSegmentWeight);
}else
arr.add(-1);
}
return arr ;
}
4 ) 初始化緩存區Segment
List<Integer> arr = getSegmentNum();
int index = 0;
for(Integer i : arr ) {
this.segments[index++] = new Segment<K, V> (segmentSize,i,builder.getStatsCounterSupplier().get());
}
5) 根據key定位緩存區
V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
int hash = hash(checkNotNull(key)); //根據key的hashcode計算得出來的值
return segments[(hash >>> segmentShift) & segmentMask].get(key, hash, loader); 找到他在緩存區
}