随笔-9  评论-3  文章-0  trackbacks-0
oscache的默认缓存实现是由4个类组成的,如下图所示:




首先来看一下是如何放入缓存的操作吧,也就是AbstractConcurrentReadCache类的#put()方法:

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


    
// 这里的第三个参数代表是否持久化缓存
    private Object put(Object key, Object value, boolean persist) {
        
if (value == null{// 默认是不支持空值的
            throw new NullPointerException();
        }


        
// 计算hash
        int hash = hash(key);
        
// hash表,其实Entry本身是一个链表的结构,也就是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{// 如果哈希表当前位置是空位
                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()是由之类去实现的
                        oldValue = remove(removeItem(), falsefalse);
                    }


                    
if (first == tab[index]) {// 这里是对可能存在的并发更新的处理
                        
// 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;

                        
// 通知具体实现值已经放入缓存的回调
                        itemPut(key);

                        
// Persist if required
                        
// 这里如果配置文件中cache.memory=false并且cache.persistence.overflow.only=true程序就进入了一个混乱的状态了
                        
// 因为内存中的Entry值为NULL,并且不会调用持久化存储
                        
// 所以这两个配置项配合的话只有3种情况了
                        
// (1) memory=true, overflow=true:使用内存缓存,溢出的数据持久化
                        
// (1) memory=true, overflow=false:使用内存缓存,溢出的数据不处理
                        
// (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);
                        }


                        
// 如果数量大于threshold(capacity * 装填因子(loadfactor))
                        if (++count >= threshold) {// 是否rehash
                            rehash();
                        }
 else {
                            recordModification(newEntry);
                        }


                        
return oldValue;
                    }
 else {
                        
// 如果当前hash表发生了变化,即发生了并发插入缓存的操作,此时需要进入这个分支
                        
// #sput()里边的逻辑和#put()是类似的
                        return sput(key, value, hash, persist);
                    }

                }

            }
 else if ((key == e.key) || ((e.hash == hash) && key.equals(e.key))) {// 如果当前的key已经存在了,更新值
                
// 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) {
                            
// 如果缓存溢出需要持久化,在缓存持久化处移除这个值
                            
// 因为现在内存中已经有这个值了,不能再持久化了
                            
// 这里因为是更新,所以按理说不会有它对应的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;
            }

        }

    }

整个的流程用代码的注释其实就可以写清楚了,注意,在更新缓存后会调用给之类的回调函数#itemPut(),另外还有参数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链表中的第一个数据
        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
                        
// 在持久化处找到数据的话需要更新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))) {// 找到了数据
                Object value = e.value;

                
if (value != null{
                    
if (NULL.equals(value)) {
                        
// Memory cache disable, use disk
                        
// 需要去缓存找数据
                        value = persistRetrieve(e.key);

                        
if (value != null{
                            
// 调用回调
                            itemRetrieved(key);
                        }


                        
return value; // fix [CACHE-13]
                    }
 else {
                        
// 调用回调
                        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;
                }


                
// 到这里其实是列表处于一个错误的状态了,重新循环
                e = first = tab[index = hash & (tab.length - 1)];
            }
 else {// 需要查看链表中的下一个元素
                e = e.next;
            }

        }

    }

其实这个#get()在一些并发控制的精妙上我也看不出来,只能留待以后水平高了的时候去研究了,现在能看懂的也只有大致的流程。

最后对3个默认缓存实现中的LRU进行下简单的分析,实现方法挺简单的,不过有一些借鉴意义。

首先,LRUCache使用LinkedHashSet来将key值进行是否最近使用的排序,越往后越是最近使用的Key

    private Collection list = new LinkedHashSet();

前面不是说在put,get,remove中都有之类的回调函数吗,这里就派上用场了


    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 臭美 阅读(2268) 评论(1)  编辑  收藏 所属分类: Cache

评论:
# Nike Air Jordan 2010-12-16 09:48 | Nike Air Jordan
看来好像很难  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: