HashTable, HashMap, ConcurrentHashMap 之间的区别

目录

一.关于三者的线程安全的问题

二.ConcurrentHashMap的优化策略

Ⅰ.锁粒度的控制

Ⅱ.ConcurrentHashMap只是给写操作加锁,读操作不加锁

Ⅲ.充分利用了CAS的特性

四.ConcurrentHashMap的扩容操作


一.关于三者的线程安全的问题

        HashMap是线程不安全的,而HashTable和ConcurrentHashMap这两者是线程安全

二.ConcurrentHashMap的优化策略

在多线程的情况下,要保证线程安全的情况下,不推荐使用HashTable,因为它是无脑的给各种方法加synchronized,对于资源的消耗比较大,性能较低

现在推荐使用的是ConcurrentHashMap,它有四个很重要的优化策略

Ⅰ.锁粒度的控制

HashTable是直接在方法外面加上synchronized,相当于是对this进行加锁,那就是相当于对哈希表对象来加锁

一个哈希表只有一个锁, 在多线程里面,无论这些线程是如何操作,这个哈希表都会产生锁冲突

而ConcurrentHashMap是每个哈希桶都有自己的锁,两个线程同时访问同一个哈希桶的时候才有锁冲突,如果不是同一个哈希桶,就没有锁冲突了,哈希桶的个数多了就大大降低了产生锁冲突的概率,性能就大大提升了

旧版本的ConcurrentHashMap使用的是分段锁,分段锁是好几个链表共用一把锁,发生锁冲突的概率比每个链表一把锁更高,代码实现起来更复杂

Ⅱ.ConcurrentHashMap只是给写操作加锁,读操作不加锁

两个线程同时修改,才会产生锁冲突

两个线程同时读,不会产生锁冲突

但是一个线程读的同时,另一个线程修改,因为读操作不加锁,所以不涉及锁冲突,但是这个操作可能有线程安全问题,即有可能读到的数据是修改了一半的数据

但是ConcurrentHashMap在设计的时候考虑到了这一点,所以能够保证读到的数据不是修改一半的数据(要么是旧版本的数据,要么是新版本的数据)

读操作中也广泛应用了volatile来保证读到的数据是及时的

Ⅲ.充分利用了CAS的特性

很多地方直接使用CAS实现的轻量级锁来实现的,比如维护元素个数,就是通过CAS而不是加锁

ConcurrentHashMap思路就是能不加就不加

ConcurrentHashMap核心优化思路是尽可能一切,降低锁冲突的概率

四.ConcurrentHashMap的扩容操作

ConcurrentHashMap对于扩容操作,进行了化整为零的操作(有些像写时拷贝)

HashTable的扩容操作:当put元素的时候,发现当前的负载因子已经超过了阈值,就需要触发扩容, 它会申请一个更大的数组,然后把之前旧的数据给搬运到新的数组上

这个操作有一个很大的问题:如果元素的个数足够多,搬运的开销就会很大,本来执行一个put操作的时间复杂度时O(1),但是触发扩容的这次put, 就可能会卡很久的时间

ConcurrentHashMap在扩容的时候,并不是直接一次性搬运完成,而是分为多次,每一次都搬运一点.

扩容过程中,旧的数组和新的数组会同时存在一段时间, 在每次进行哈希表操作的时候,都会把旧的内存上的元素搬运一部分到新的空间上,直到最终搬运完成, 才会释放旧的空间

如果需要查询元素,则会在旧的和新的数组上面一起查

如果需要新增元素,则直接往新的数组上插入

如果需要删除元素,则直接删了不用搬运

这个操作就像是爬楼梯一样,如果你想直接跳上二楼很难,而如果时爬楼梯,每一步都上升一点,积少成多就上去了

另外HashMap的key允许为null,而HashTable和ConcurrentHashMap的key不能为null

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山涧晴岚.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值