分布式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版本的实现。

相关文章
ThreadLocal详解
文章详细讨论了Java中的`ThreadLocal`,包括它的基本使用、定义、内部数据结构`ThreadLocalMap`、主要方法(set、get、remove)的源码解析,以及内存泄漏问题和避免策略。`ThreadLocal`提供了线程局部变量,确保多线程环境下各线程变量的独立性,但不当使用可能导致内存泄漏,因此建议在不再需要`ThreadLocal`变量时调用其`remove`方法。
181 2
ThreadLocal详解
Flink CDC数据读取问题之一致性如何解决
Flink CDC 使用Change Data Capture (CDC)技术从数据库捕获变更事件,并利用Flink的流处理能力确保数据读取一致性。相较于传统工具,它具备全增量一体化数据集成能力,满足实时性需求。在实践中解决了高效数据同步、稳定同步大量表数据等问题。应用场景包括实时数据同步、实时数据集成等。快速上手需学习基本概念与实践操作。未来发展方向包括提升效率与稳定性,并依据用户需求持续优化。
254 1
MQ的顺序性保证:顺序队列、消息编号、分布式锁,一文全掌握!
【8月更文挑战第24天】消息队列(MQ)是分布式系统的关键组件,用于实现系统解耦、提升可扩展性和可用性。保证消息顺序性是其重要挑战之一。本文介绍三种常用策略:顺序队列、消息编号与分布式锁,通过示例展示如何确保消息按需排序。这些方法各有优势,可根据实际场景灵活选用。提供的Java示例有助于加深理解与实践应用。
510 2
阿里云免费SSL证书和收费版SSL证书有什么区别?
阿里云提供免费与收费SSL证书,前者有效期仅3个月,适合个人网站或测试使用;后者有效期至少1年,具备更高安全等级、良好兼容性及OCSP验证稳定性等优势,适用于企业网站,尤其政府、金融等领域建议选用OV或EV型证书以确保数据与身份认证安全。详细了解与报价请访问SSL证书官方页面。
1328 2
c#集合_键值对Dictionary & SortedList
在 C# 中,键值对是一种常见的数据结构,可以使用不同的集合类实现。以下是常用的键值对集合类::一种使用哈希表实现的键值对集合。它通过将键哈希为桶号,然后将值存储在桶中进行快速查找。:一种基于数组实现的键值对集合。它会将键值对按照键排序并存储在数组中,以支持快速访问、查找和枚举。:一种使用红黑树实现的键值对集合。它能够按照键的排序进行快速查找,也可以快速地插入和删除键值对,并且该树具备自平衡的特性,使得插入、删除和搜索性能都非常优秀。
372 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问