ConcurrentHashMap总结

ConcurrentHashMap总结

其实我是不想写的,很多人已经写的很详细了,自己再写有些多次一举。这里只是做个总结。
  JDK1.6,JDK1.7,JDK1.8的实现细节都有所不同,下面以JDK1.7代码为主,


ConcurrentHashmap和HashTable的区别:

  • HashTable依赖synchronized关键字实现线程安全的,但是synchronized的性能十分低下,对get操作都要做加锁操作,使用的是全局锁,相当于数据库中使用的表锁,锁的颗粒度越大,并发效果越差,但是安全性高;
  • ConcurrentHashmap引入了分段锁的概念,对不同的数据加不同的锁,相当于数据库中的行锁,锁的颗粒度变小,并发效果变好,但是ConcurrentHashmap带来的是一致性是弱一致性的,如果对一致性要求非常高,还是要是用HashTable或者Collections提供的synchronizedHashMap。

盗图一张
ConcurrentHashMap结构图


两个内部类来实现

HashEntry类
static final class HashEntry<K, V> {
        final int hash;
        final K key;
        volatile V value;
     volatile ConcurrentHashMap.HashEntry<K, V> next;
    }
Segment类
static final class Segment<K, V> extends ReentrantLock implements Serializable {
        transient volatile ConcurrentHashMap.HashEntry<K, V>[] table;
        transient int threshold;
        final float loadFactor;

        Segment(float lf, int threshold, ConcurrentHashMap.HashEntry<K, V>[] tab) {
            this.loadFactor = lf;
            this.threshold = threshold;
            this.table = tab;
        }
    }

HashEntry用来保存键值对,Segment继承自ReentrantLock,在这里充当锁。


定位

源代码的一些位运算我真没看懂,思路是:
hash算法获取hash值,然后和segment长度减1(初始值是16,减1就是15)做与操作,得到对应的segment,然后再次hash计算对应的桶的位置。也就是做了两次hash算法,双hash算法是为了确保数据尽可能的分散,一次hash算法有很大的可能发生冲突。


get操作

先计算key的hash值,然后使用这个哈希值定位到segment,再通过哈希算法定位到元素,get的操作是很高效的,原因在于不需要加锁,不加锁而不会发生并发的原因在于:

 volatile V value;
 volatile ConcurrentHashMap.HashEntry<K, V> next;
  • volatiles保持可见性;
  • jvm内存模型happen before原则,对volatile字段的写入操作先于读操作,即使两个线程同时修改和获取volatile变量,get操作也能拿到最新的值,这是用volatile替换锁的经典应用场景。;
  • 通过Unsafe对象的getObjectVolatile()方法提供的原子读语义,来获得Segment以及对应的链表,但由于遍历过程中其他线程可能对链表结构做了调整,因此get和containsKey返回的可能是过时的数据,这一点是ConcurrentHashMap在弱一致性上的体现。如果要求强一致性,那么必须使用Collections.synchronizedMap()方法。

put操作

put操作是加锁了,所以在安全性上没有问题的,但怎么保证在put的时候,可以保证get的正确性呢?JDK1.7使用了Unsafe的原子性操作保持内存的可见性。


remove操作

这个和put类似了


size操作

这是基于整个ConcurrentHashMap来进行操作的,原理是:首先不加锁循环执行以下操作:循环所有的Segment(通过Unsafe的getObjectVolatile()以保证原子读语义),获得对应的值以及所有Segment的modcount之和。如果连续两次所有Segment的modcount和相等,则过程中没有发生其他线程修改ConcurrentHashMap的情况,返回获得的值。(这个和CAS原理是一样的)


总结

在使用锁协调多线程环境下同步访问时,减少对锁的竞争可以有效提高并发度,减少对锁的竞争有两种方法:

  • 减少请求锁的次数
  • 减少持有锁的时间

ConcurrentHashMap中通过以下三中措施提高并发性能:

  • 分段锁Segment的使用可以有效减少发生竞争的可能性
  • HashEntry中value和next域的volatile属性可以减少持有锁的时间
  • HashEntry倒序更新的方式可以减少访问次数和持有锁的时间
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值