oscache的默認緩存實現(xiàn)是由4個類組成的,如下圖所示:
首先來看一下是如何放入緩存的操作吧,也就是AbstractConcurrentReadCache類的#put()方法:

public Object put(Object key, Object value) {
return put(key, value, true);
}

// 這里的第三個參數(shù)代表是否持久化緩存

private Object put(Object key, Object value, boolean persist) {

if (value == null) {// 默認是不支持空值的
throw new NullPointerException();
}

// 計算hash
int hash = hash(key);
// hash表,其實Entry本身是一個鏈表的結(jié)構(gòu),也就是hash桶
Entry[] tab = table;

// 將hash值與hash表的長度按位與得到初始位置
int index = hash & (tab.length - 1);

// first指的是hash表中Entry鏈表的第一個元素
Entry first = tab[index];
Entry e = first;


for (;;) {

if (e == null) {// 如果哈希表當(dāng)前位置是空位

synchronized (this) {
tab = table;

Object oldValue = null;

// Remove an item if the cache is full

if (size() >= maxEntries) {// 如果緩存已滿,需要挑選一個Entry移出
// part of fix CACHE-255: method should return old value
// 挑選要移出的key的方法#removeItem()是由之類去實現(xiàn)的
oldValue = remove(removeItem(), false, false);
}


if (first == tab[index]) {// 這里是對可能存在的并發(fā)更新的處理
// Add to front of list
Entry newEntry = null;


if (memoryCaching) {
newEntry = new Entry(hash, key, value, first);

} else {
newEntry = new Entry(hash, key, NULL, first);
}

tab[index] = newEntry;

// 通知具體實現(xiàn)值已經(jīng)放入緩存的回調(diào)
itemPut(key);

// Persist if required
// 這里如果配置文件中cache.memory=false并且cache.persistence.overflow.only=true程序就進入了一個混亂的狀態(tài)了
// 因為內(nèi)存中的Entry值為NULL,并且不會調(diào)用持久化存儲
// 所以這兩個配置項配合的話只有3種情況了
// (1) memory=true, overflow=true:使用內(nèi)存緩存,溢出的數(shù)據(jù)持久化
// (1) memory=true, overflow=false:使用內(nèi)存緩存,溢出的數(shù)據(jù)不處理
// (1) memory=false, overflow=false:使用持久化緩存


if (persist && !overflowPersistence) {// 如果需要持久化保存
persistStore(key, value);
}

// If we have a CacheEntry, update the group lookups

if (value instanceof CacheEntry) {
// 更新緩存的分組信息,其實AbstractConcurrentReadCache
// 用一個HashMap保存了分組名和各個key之間的一個映射 groupname -> Set<Key>
updateGroups(null, (CacheEntry) value, persist);
}

// 如果數(shù)量大于threshold(capacity * 裝填因子(loadfactor))

if (++count >= threshold) {// 是否rehash
rehash();

} else {
recordModification(newEntry);
}

return oldValue;

} else {
// 如果當(dāng)前hash表發(fā)生了變化,即發(fā)生了并發(fā)插入緩存的操作,此時需要進入這個分支
// #sput()里邊的邏輯和#put()是類似的
return sput(key, value, hash, persist);
}
}

} else if ((key == e.key) || ((e.hash == hash) && key.equals(e.key))) {// 如果當(dāng)前的key已經(jīng)存在了,更新值
// synch to avoid race with remove and to
// ensure proper serialization of multiple replaces

synchronized (this) {
tab = table;

Object oldValue = e.value;

// [CACHE-118] - get the old cache entry even if there's no
// memory cache
// oldValue為NULL代表了是磁盤緩存

if (persist && (oldValue == NULL)) {
// 在磁盤里去的緩存值
oldValue = persistRetrieve(key);
}


if ((first == tab[index]) && (oldValue != null)) {

if (memoryCaching) {
// 緩存更新值
e.value = value;
}

// Persist if required

if (persist && overflowPersistence) {
// 如果緩存溢出需要持久化,在緩存持久化處移除這個值
// 因為現(xiàn)在內(nèi)存中已經(jīng)有這個值了,不能再持久化了
// 這里因為是更新,所以按理說不會有它對應(yīng)的overflow緩存的啊?
persistRemove(key);

} else if (persist) {// 持久化保存
persistStore(key, value);
}

updateGroups(oldValue, value, persist);
itemPut(key);

return oldValue;

} else {
return sput(key, value, hash, persist);
}
}

} else {// 將e指向Entry鏈表的下一個項目
e = e.next;
}
}
}
整個的流程用代碼的注釋其實就可以寫清楚了,注意,在更新緩存后會調(diào)用給之類的回調(diào)函數(shù)#itemPut(),另外還有參數(shù)cache.memory和cache.persistence.overflow.only對流程的影響。
下面看下#get(),這里#remove()就不寫了其實過程反倒和#get()也差不多:

public Object get(Object key) {

if (log.isDebugEnabled()) {
log.debug("get called (key=" + key + ")");
}

// 計算hash
int hash = hash(key);


/**//*
* Start off at the apparently correct bin. If entry is found, we need
* to check after a barrier anyway. If not found, we need a barrier to
* check if we are actually in right bin. So either way, we encounter
* only one barrier unless we need to retry. And we only need to fully
* synchronize if there have been concurrent modifications.
*/
// 計算在hash表中的位置
Entry[] tab = table;
int index = hash & (tab.length - 1);
// Entry鏈表中的第一個數(shù)據(jù)
Entry first = tab[index];
Entry e = first;


for (;;) {

if (e == null) {
// If key apparently not there, check to
// make sure this was a valid read
// key沒找到,再次查看hash表確定是否真的找不到了
tab = getTableForReading();


if (first == tab[index]) {
// Not in the table, try persistence
// 試著在持久化處找
Object value = persistRetrieve(key);


if (value != null) {
// Update the map, but don't persist the data
// 在持久化處找到數(shù)據(jù)的話需要更新hash表,但不去重新持久化
put(key, value, false);
}

return value;

} else {
// Wrong list -- must restart traversal at new first
e = first = tab[index = hash & (tab.length - 1)];
}
}
// checking for pointer equality first wins in most applications

else if ((key == e.key) || ((e.hash == hash) && key.equals(e.key))) {// 找到了數(shù)據(jù)
Object value = e.value;


if (value != null) {

if (NULL.equals(value)) {
// Memory cache disable, use disk
// 需要去緩存找數(shù)據(jù)
value = persistRetrieve(e.key);


if (value != null) {
// 調(diào)用回調(diào)
itemRetrieved(key);
}

return value; // fix [CACHE-13]

} else {
// 調(diào)用回調(diào)
itemRetrieved(key);

return value;
}
}

// Entry was invalidated during deletion. But it could
// have been re-inserted, so we must retraverse.
// To avoid useless contention, get lock to wait out
// modifications
// before retraversing.

synchronized (this) {
tab = table;
}

// 到這里其實是列表處于一個錯誤的狀態(tài)了,重新循環(huán)
e = first = tab[index = hash & (tab.length - 1)];

} else {// 需要查看鏈表中的下一個元素
e = e.next;
}
}
}
其實這個#get()在一些并發(fā)控制的精妙上我也看不出來,只能留待以后水平高了的時候去研究了,現(xiàn)在能看懂的也只有大致的流程。
最后對3個默認緩存實現(xiàn)中的LRU進行下簡單的分析,實現(xiàn)方法挺簡單的,不過有一些借鑒意義。
首先,LRUCache使用LinkedHashSet來將key值進行是否最近使用的排序,越往后越是最近使用的Key
private Collection list = new LinkedHashSet();
前面不是說在put,get,remove中都有之類的回調(diào)函數(shù)嗎,這里就派上用場了

protected void itemRetrieved(Object key) {

while (removeInProgress) {

try {
Thread.sleep(5);

} catch (InterruptedException ie) {
}
}

// 這里改變了list中元素的順序

synchronized (list) {
list.remove(key);
list.add(key);
}
}


protected void itemPut(Object key) {
// 這里改變了list中元素的順序

synchronized (list) {
list.remove(key);
list.add(key);
}
}
這樣,在緩存已滿需要查找待移除的Key時,就可以使用list的順序了,很簡單,但是很有效。

private Object removeFirst() {
Object toRemove = null;


synchronized (list) { // A further fix for CACHE-44 and CACHE-246
Iterator it = list.iterator();
toRemove = it.next();
it.remove();
}

return toRemove;
}
posted on 2010-12-16 09:19
臭美 閱讀(2276)
評論(1) 編輯 收藏 所屬分類:
Cache