分布式Map中实现引用计数

简介:

前言

在《 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版本的实现。

相关文章
|
安全 Java 容器
Java多线程编程中的线程安全集合:保护数据的铁壁
Java多线程编程中的线程安全集合:保护数据的铁壁
167 1
|
存储 算法 Java
HashMap 之底层数据结构和扩容机制
HashMap 之底层数据结构和扩容机制
988 1
|
3月前
|
存储 缓存 索引
从底层数据结构和CPU缓存两方面剖析LinkedList的查询效率为什么比ArrayList低
本文详细对比了ArrayList和LinkedList的查询效率,从底层数据结构和CPU缓存两个方面进行分析。ArrayList基于动态数组,支持随机访问,查询时间复杂度为O(1),且CPU缓存对其友好;而LinkedList基于双向链表,需要逐个节点遍历,查询时间复杂度为O(n),且CPU缓存对其帮助不大。文章还探讨了CPU缓存对数组增删操作的影响,指出缓存主要作用于读取而非修改。通过这些分析,加深了对这两种数据结构的理解。
53 2
|
4月前
|
存储 算法 Java
深入剖析HashMap:理解Hash、底层实现与扩容机制
【9月更文挑战第6天】在Java编程中,`HashMap`是一个常用的数据结构,其高效性和可靠性依赖于深入理解哈希、底层实现及扩容机制。哈希通过散列算法将键映射到数组索引,采用链表或红黑树处理冲突;底层实现结合数组与链表,利用2的幂次方长度加快定位;扩容机制在元素数量超过负载因子与数组长度乘积时触发,通过调整初始容量和负载因子可优化性能。
127 3
|
7月前
|
存储 安全 容器
ConcurrentHashMap底层详解
ConcurrentHashMap底层详解
323 2
ConcurrentHashMap底层详解
|
8月前
|
存储 Java Serverless
谈谈我对HashMap扩容机制的理解及底层实现
谈谈我对HashMap扩容机制的理解及底层实现
|
8月前
|
存储 安全
ConcurrentHashMap 底层具体实现
ConcurrentHashMap 底层具体实现
|
8月前
|
存储 安全 Java
Hashtable和HashMap:差异,数据结构概述,以及JDK的影响
Hashtable和HashMap:差异,数据结构概述,以及JDK的影响
56 0
|
8月前
|
安全 容器
线程安全的集合类(多线程环境下使用ArrayList、队列及哈希表)
线程安全的集合类(多线程环境下使用ArrayList、队列及哈希表)
|
存储 算法 安全
HashMap的遍历方式及底层原理
HashMap的遍历方式及底层原理