前言
在《
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) && 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 閱讀(5491)
評論(0) 編輯 收藏 所屬分類:
Core Java