ConcurrentHashMap的理解

目录

前言:

一、为什么不推荐使用Hashtable,和HashMap有什么区别

 1.为什么不用Hashtable:

2.HashTable和HashMap有什么区别:

        1.继承体系上看:(先放一张单列集合和双列集合的大图)

        2.性能方面:         

        3.值设置方面:

 二、既然ConcurrentHashMap能保证线程安全,他是怎么做到的?

        1. jdk1.8之前的实现方式:

        2.jdk1.8之后的实现方式:

        3. jdk1.8之后的ConcurrentHashMap的get方法为什么不被阻塞


前言:

        我们都知道HashMap不是线程安全的,如果想用线程安全的HashMap,我们可以采用Hashtable(不推荐,性能低)或者ConcurrentHashMap。今天我就来分享一下ConcurrentHashMap的一些个人见解。

一、为什么不推荐使用Hashtable,和HashMap有什么区别

 1.为什么不用Hashtable:

        首先,我们要知道HashTable它的保证线程安全的方式很粗暴,他就是单纯把自己的方法加Synchronized关键字,我们知道Synchronized是一个重量级锁,开销大;其次,他直接加方法上锁的粒度太粗,性能差。其实官方也不推荐使用了,都推荐使用接下来介绍的ConcurrentHashMap

2.HashTable和HashMap有什么区别:

        1.继承体系上看:(先放一张单列集合和双列集合的大图)

                

        粗略的看,我们发现Hashtable和HashMap好像都实现了Map接口,但其实HashMap继承自AbstractMap类(AbstractMap 是 Java 中 Map 接口的抽象基类,它提供了部分 Map 接口的默认实现。AbstractMap 可以帮助你更容易地实现自定义的 Map 类),而Hashtable作为版本弃子,已经被放弃了。(见下图)

        HashMap的:

        Hashtable的:

        2.性能方面:         

                就之前说的Synchronized实现线程安全,所以导致Hashtable在单线程下不如HashMap,多线程打不过ConcurrentHashMap,真惨啊 

        3.值设置方面:

                HashMap是支持Key(当然key要保证唯一,只能有一个key是null)和value等于null的,而Hashtable的话,会直接报错(NullPointerExecption)也就是空指针异常

                HashMap存(null,null)这个键值对,能正常使用:

                HashTable的情况:        

 二、既然ConcurrentHashMap能保证线程安全,他是怎么做到的?

        1. jdk1.8之前的实现方式:

                此时ConcurrentHashMap是通过一个思想——分段锁 来实现的。可以简单理解为我们把原先的HashMap数组分为了多段(每个段可以理解为一个独立的HashMap),我们如果要操作某个段部分的数据,我们只需对这个段加锁,这样别的段是不受影响的,可以被别的线程所使用现在我们去对比下之前的HashTable,是不是觉得Hashtable low爆了,所以ConcurrentHashMap并发性能要更高。这还仅仅是1.8之前的版本。

        2.jdk1.8之后的实现方式:

                那些大佬觉得对之前的实现方式不太满意,分段锁还是粒度太大了,并发性不够。于是他们又整出了一个新的方式。我们了解到HashMap桶下面不是挂红黑树或者链表吗,1.8之后就对这玩意下手了,直接锁住头结点因为我们不论是链表还是红黑树,你都得从头结点开始查吧,我直接锁住这里就ok了,这样每一个桶相当于之前的分段锁了,粒度更细,并发性能更高。

                举个例子:就假设数组是16长度,分段锁分为了4段,1.8之后就直接分为了16段。                          比如我要查索引位置=2的桶下的节点,1.8之前就锁住了第一段,而1.8之后只需要锁住桶下的第一个节点就OK了,这样别的15个节点都是允许增删改查的,并发性不就上去了。

                当然,这是针对桶下有链表或者红黑树的情况,如果是单纯一个桶,下面没有链表和红黑树,多线程访问,怎么保证这个桶(也就是该索引位置)的线程安全呢?

                这时候,操作系统就出手了——CAS(Compare and swap比较并交换,一种乐观锁思想)分段锁,操作系统出手就放心交给他就好了。

                简单介绍下CAS:

                他有3个参数:1.旧值 old 2.当前读取值 new 3.要更改的值write

                我们要做修改,先去读一次得到old,把旧值old带着去写,写的同时判断当前值 new 是否等于之前读到的旧值 old(如果 new=old 表示这期间,这个值没有被动过,我就可以直接操作修改,改为write;如果不等,那我就下次再来)

                当然CAS引发的ABA的问题,有兴趣的可以自己去了解一下

        3. jdk1.8之后的ConcurrentHashMap的get方法为什么不被阻塞

                这个问题我也是参考了别的大佬的回答

                我们可以看到,链表转为红黑树之后,他并不是只有红黑树这么一个东西,他其实还备份了一个双向链表,这就保证了get不被阻塞。

                比如,我们有一个线程在对图中这个红黑树的节点做增删改查,红黑树在做自平衡,并且,因为写操作会锁住红黑树的根节点,所以我们是不能通过红黑树查数据的,这时候我们其实是通过查双向链表来实现get方法的,所以get方法不会被阻塞。

                感兴趣的可以看看这个老哥的博客,ConcurrentHashMap源码这块讲的挺好的:

                大佬的ConcurrentHashMap源码讲解传送门

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值