浅析HashMap与ConcurrentHashMap的线程安全性

本文探讨HashMap和ConcurrentHashMap的线程安全性。HashMap非线程安全,因多线程并发修改可能导致数据丢失或异常。ConcurrentHashMap通过锁分段技术实现线程安全,提高并发性能。
摘要由CSDN通过智能技术生成

本文要解决的问题:

最近无意中发现有很多对Map尤其是HashMap的线程安全性的话题讨论,在我的理解中,对HashMap的理解中也就知道它是线程不安全的,以及HashMap的底层算法采用了链地址法来解决哈希冲突的知识,但是对其线程安全性的认知有限,故写这篇博客的目的就是让和我一样对这块内容不熟悉的小伙伴有一个对HashMap更深的认知。


哈希表


在数据结构中有一种称为哈希表的数据结构,它实际上是数组的推广。如果有一个数组,要最有效的查找某个元素的位置,如果存储空间足够大,那么可以对每个元素和内存中的某个地址对应起来,然后把每个元素的地址用一个数组(这个数组也称为哈希表)存储起来,然后通过数组下标就可以直接找到某个元素了。这种方法术语叫做直接寻址法。这种方法的关键是要把每个元素和某个地址对应起来,所以如果当一组数据的取值范围很大的时候,而地址的空间又有限,那么必然会有多个映射到同一个地址,术语上称为哈希冲突,这时映射到同一个地址的元素称为同义词。毕竟,存储空间有限,所以冲突是不可避免的,但是可以尽量做到减少冲突。目前有两种比较有效的方法来解决哈希冲突:

  • 链地址法
  • 开放地址法

这里简要说明一下开放地址法,顾名思义,就是哈希表中的每个位置要么存储了一个元素要么为NULL。当数据比较多的时候,查找一个元素挺费事的,但是可以使用探测的方法进行查找。这个话题与本主题关系不大,感兴趣的小伙伴可以自行研究。

链地址法

为什么要把链地址法单独拿出来呢?因为后面有用。
链地址法的大概思想是:对于每个关键字,使用哈希函数确定其在哈希表中位置(也就是下标),如果该位置没有元素则直接映射到该地址;如果该位置已经有元素了,就把该元素连接到已存在元素的尾部,也就是一个链表,并把该元素的next设置为null。这样的话,每个哈希表的位置都可能存在一个链表,这种方式要查找某个元素效率比较高,时间复杂度为O(1+a),a为哈希表中每个位置链表的平均长度。这里需要假设每个元素都被等可能映射到哈希表中的任意一个位置。

下面这张图展示了链地址法的过程:

链地址法

HashMap


HashMap底层实现

HashMap允许使用null作为key或者value,并且HashMap不是线程安全的,除了这两点外,HashMap与Hashtable大致相同,下面是官方API对HashMap的描述:

Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

如果有多个线程对Hash映射进行访问,那么至少有一个线程会对哈希映射进行结构的修改:

结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改

那么很显然,当多个线程同时(严格来说不能称为同时,因为CPU每次只能允许一个线程获取资源,只不过时间上非常短,CPU运行速度很快,所以理解为同时)修改哈希映射,那么最终的哈希映射(就是哈希表)的最终结果是不能确定的,这只能看CPU心情了。如果要解决这个问题,官方的参考方案是保持外部同步,什么意思?看下面的代码就知道了:

Map m = Collections.synchronizedMap(new HashMap(...));

但是不建议这么使用,因为当多个并发的非同步操作修改哈希表的时候,最终结果不可预测,所以使用上面的方法创建HashMap的时候,当有多个线程并发访问哈希表的情况下,会抛出异常,所以并发修改会失败。比如下面这段代码:

for (int i = 0
  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值