隨筆-9  評論-3  文章-0  trackbacks-0
          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(), falsefalse);
                              }


                              
          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

          評論:
          # Nike Air Jordan 2010-12-16 09:48 | Nike Air Jordan
          看來好像很難  回復(fù)  更多評論
            
          主站蜘蛛池模板: 兴业县| 呈贡县| 清原| 唐海县| 教育| 白沙| 怀集县| 关岭| 吉林市| 山东省| 东城区| 苏尼特右旗| 凌源市| 闻喜县| 神池县| 佛学| 松桃| 芒康县| 蒙山县| 湘阴县| 新野县| 福建省| 德阳市| 浠水县| 苏尼特左旗| 太仆寺旗| 拉萨市| 孟津县| 梨树县| 泸溪县| 阿尔山市| 稻城县| 额尔古纳市| 通江县| 玉田县| 集贤县| 陕西省| 张掖市| 阜新市| 多伦县| 尚志市|