上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0

          前言

          在《ReferenceCountSet無鎖實現》中,詳細介紹了如何在一個進程中實現一個無鎖版本的ReferenceCountSet(或者說是在自己的代碼里沒有鎖),但是最近遇到一個問題,如果是在分布式的環境中呢?如何實現這個引用計數?這個問題如果從頭開始寫,會是一個比較復雜的問題,在實際中,我們可以使用ZooKeeper設置時的version機制來實現,即CAS(Compare-And-Set)。這是一個本人在實際項目中遇到的一個問題,但是會更簡單一些,因為在我們的項目中,我們使用GemFire,即已經有一個現成的分布式Map了(在Gemfire中叫做Region),所以問題簡化成如果如何使用一個分布式Map實現引用計數?

          實現

          如果對ConcurrentMap接口比較熟悉的話,這個其實是一個比較簡單的問題。在ConcurrentMap中最主要的就是引入幾個CAS相關的操作:
          public interface ConcurrentMap<K, V> extends Map<K, V> {
              V putIfAbsent(K key, V value);
              boolean remove(Object key, Object value);
              boolean replace(K key, V oldValue, V newValue);
              V replace(K key, V value);
          }
          在《ReferenceCountSet無鎖實現》中我們只需要使用putIfAbsent就可以了,剩下的實現可以交給AtomicInteger提供的CAS來實現,因為它是在同一個進程中,但是如果在分布式的環境中就不能使用這個AtomicInteger,這個時候應該怎么辦呢?其實這個時候我們就可以求助于replace方法了。replace方法的注釋中這樣描述:
              /**
               * Replaces the entry for a key only if currently mapped to a given value.
               * This is equivalent to
               * <pre>
               *   if (map.containsKey(key) &amp;&amp; map.get(key).equals(oldValue)) {
               *       map.put(key, newValue);
               *       return true;
               *   } else return false;</pre>
               * except that the action is performed atomically.
               *
               * 
          @param key key with which the specified value is associated
               * 
          @param oldValue value expected to be associated with the specified key
               * 
          @param newValue value to be associated with the specified key
               * 
          @return <tt>true</tt> if the value was replaced
               * 
          @throws UnsupportedOperationException if the <tt>put</tt> operation
               *         is not supported by this map
               * 
          @throws ClassCastException if the class of a specified key or value
               *         prevents it from being stored in this map
               * 
          @throws NullPointerException if a specified key or value is null,
               *         and this map does not permit null keys or values
               * 
          @throws IllegalArgumentException if some property of a specified key
               *         or value prevents it from being stored in this map
               
          */
              boolean replace(K key, V oldValue, V newValue);

          在ConcurrentMap的value中我們只需要給Integer,然后用replace去不斷的嘗試,即自己實現一個CAS:
          private int incrementRefCount(Object key) {
              do {
                  Integer curCount = distributedMap.get(key);
                  if (curCount == null) {
                      curCount = distributedMap.putIfAbsent(key, new Integer(1));
                      if (curCount == null) {
                          return 1;
                      }
                  }
                  
                  Integer newCount = new Integer(curCount.intValue() + 1);
                  if (distributedMap.replace(key, curCount, newCount)) {
                      return newCount;
                  }
              } while (true);
          }

          主要邏輯就是這樣了,其實比較簡單,只是之前沒有遇到過這個問題,所以感覺可以記錄下來。或許什么時候補充一下ZooKeeper版本的實現。
          posted on 2015-04-20 20:30 DLevin 閱讀(5494) 評論(0)  編輯  收藏 所屬分類: Core Java
          主站蜘蛛池模板: 永修县| 内黄县| 湛江市| 交口县| 苏尼特右旗| 彭泽县| 泾川县| 三河市| 静安区| 城固县| 明星| 葫芦岛市| 无极县| 阳泉市| 甘南县| 漠河县| 沙河市| 独山县| 晋州市| 勃利县| 佛学| 蓬安县| 饶平县| 汪清县| 清徐县| 大方县| 綦江县| 玉环县| 宝兴县| 大安市| 渝中区| 阿拉善右旗| 桑日县| 伊金霍洛旗| 通渭县| 米泉市| 邯郸市| 常山县| 永福县| 凌云县| 普宁市|