zoukankan      html  css  js  c++  java
  • oscache-3

    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;
        }
  • 相关阅读:
    Models(Pascal)
    Summer Plan(挖坑待填)
    C++之指针
    QuickPower快速幂
    codevs 1231最优布线问题
    颓废了1年+,今天开始勤(tui)奋(fei)啦
    l'Hopital法则
    相律
    小意外
    一种改进的动力学处理方法
  • 原文地址:https://www.cnblogs.com/wangjianbg/p/3445492.html
Copyright © 2011-2022 走看看