HashMap源码分析系列 -- 第四弹:HashMap多线程解决方案

HashMap源码分析

笔记首页

序号内容链接地址
1HashMap的继承体系,HashMap的内部类,成员变量https://blog.csdn.net/weixin_44141495/article/details/108327490
2HashMap的常见方法的实现流程https://blog.csdn.net/weixin_44141495/article/details/108329558
3HashMap的一些特定算法,常量的分析https://blog.csdn.net/weixin_44141495/article/details/108305494
4HashMap的线程安全问题(1.7和1.8)https://blog.csdn.net/weixin_44141495/article/details/108250160
5HashMap的线程安全问题解决方案https://blog.csdn.net/weixin_44141495/article/details/108420327
6Map的四种遍历方式,以及删除操作https://blog.csdn.net/weixin_44141495/article/details/108329525
7HashMap1.7和1.8的区别https://blog.csdn.net/weixin_44141495/article/details/108402128

HashMap的线程安全问题解决方案

前言

我们知道Jdk1.7有,HashMap链表死循环问题,属于严重的BUG。那么Jdk1.8采用头插法,不会出现链表死循环的问题,那么1.8就没有线程安全问题吗?

我们知道线程安全问题会出现在修改数据的情况下,比如put,remove方法,都会出现线程安全问题。

举个例子:

两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的,当线程A执行完第六行代码if ((p = tab[i = (n - 1) & hash]) == null)后由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正常的插入,然后线程A获得时间片,由于之前已经进行了hash碰撞的判断,所有此时不会再进行判断,而是直接进行插入,这就导致了线程B插入的数据被线程A覆盖了,从而线程不安全。

image-20200905144048039

解决方案

使用并发环境安全的集合框架

Hashtable
public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {
}

简单看一下源码

我们看到,Hashtable是用synchronized关键字保证线程安全的。

image-20200905151440383

强化HashMap

使用Collections工具类的synchronizedMap方法,将HashMap改造为一个线程安全的Map。

Collections.synchronizedMap(new HashMap<String,String>());

简单看一下源码

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
}

我们看到这个方法无非就是原来的方法进一步包装,多了一个synchronized关键字,和Hashtable原理一样。

image-20200905152110182

ConcurretnHashMap

在ConcurrentHashMap中有个重要的概念就是Segment。我们知道HashMap的结构是数组+链表形式,从图中我们可以看出其实每个segment就类似于一个HashMap。Segment包含一个HashEntry数组,数组中的每一个HashEntry既是一个键值对,也是一个链表的头节点。在ConcurrentHashMap中有2的N次方个Segment,共同保存在一个名为segments的数组当中。可以说,ConcurrentHashMap是一个二级哈希表。在一个总的哈希表下面,有若干个子哈希表。
为什么说ConcurrentHashMap的性能要比HashTable好,HashTables是用全局同步锁,而CconurrentHashMap采用的是锁分段,每一个Segment就好比一个自治区,读写操作高度自治,Segment之间互不干扰。

image-20200905152639433

Case1:不同Segment的并发写入
这里写图片描述
不同Segment的写入是可以并发执行的。
Case2:同一Segment的一写一读
这里写图片描述
同一Segment的写和读是可以并发执行的。
Case3:同一Segment的并发写入
这里写图片描述

简单看一下源码

使用大量的volatile关键字来保证可见性

image-20200905152934997

remove方法

在会出现线程问题的方法也是采用synchronized关键字保证线程安全的,只不过多了一些条件判断,用"智商"换时间。

image-20200905153043293

put方法

image-20200905153115281

使用安全的方式使用HashMap

读写分离
  • 没想好怎么写
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值