关于HashMap、Hashtable、ConcurrentHashMap等区别、ConcurrentHashMap实现线程安全的知识点总结

本文详细对比了Java中ArrayList、LinkedList、Vector等列表类的区别,分析了HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap等映射类的特点与应用场景,探讨了HashMap的线程安全问题及解决方案,深入讲解了ConcurrentHashMap的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、ArrayList、LinkedList、Vector的区别

二、HashMap和Hashtable的区别

三、HashMap和ConcurrentHashMap的区别

四、HashMap和LinkedHashMap的区别

五、HashMap是非线程安全的原因及实现线程安全的方法

六、ConcurrentHashMap实现线程安全的原理


一、ArrayList、LinkedList、Vector的区别

1、ArrayList、LinkedList和Vector都是实现了List接口。

2、其中,ArrayList和Vector底层是用数组实现的,因此可以用序号下标来访问他们,查找的效率高,一般数组的大小比要插入的数据大数量要大。

3、LinkedList的底层使用双向链表实现的(含有头结点),因此插入和删除的效率高。

4、在多线程并发的时候,ArrayList和LinkedList是非线程安全的,并且是不同步的。Vector的所有方法都用了synchronized方法,是线程安全的,但是vector中的方法组合起来使用不是线程安全的。

 

二、HashMap和Hashtable的区别

 1、Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable(Hashtable通过对整个表上锁实现线程安全,因此效率比较低),但是要使用HashMap的话就要自己增加同步处理了;

2、继承不同:Hashtable基于Dictionary类 ,HashMap基于AbstractMap,而AbstractMap基于Map接口的实现;

3、Hashtable中key和value都不允许为null,遇到null,直接返回 NullPointerException ;HashMap中key和value都允许为null,遇到key为null的时候,调用putForNullKey方法进行处理,而对value没有处理;

4、两个遍历方式的内部实现上不同。Hashtable、HashMap都使用了Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式;

5、Hashtable中hash数组默认大小是11,扩充方式是old*2+1 ;HashMap中hash数组的默认大小是16,而且一定是2的指数。

 

三、HashMap和ConcurrentHashMap的区别

1、Hashmap本质是数组加链表。根据key取得hash值,然后计算出数组下标,如果多个key对应到同一个下标,就用链表串起来,新插入的在前面。在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,所以HashMap是线程不安全的;

2、ConcurrentHashMap:在HashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),然后每次操作对一个segment加锁,避免多线程锁的几率,实现线程安全,提高并发效率。

 

四、HashMap和LinkedHashMap的区别

1、LinkedHashMap有序的,有插入顺序和访问顺序 ,HashMap无序的;

2、LinkedHashMap内部维护着一个运行于所有条目的双向链表 ,HashMap内部维护着一个单链表。

总结归纳:linkedMap在于存储数据你想保持进入的顺序与被取出的顺序一致的话,优先考虑LinkedMap,HashMap键只能允许一条为空,value可以允许为多条为空,键唯一,但值可以多个。

 

五、HashMap是非线程安全的原因及实现线程安全的方法

原文地址:https://blog.youkuaiyun.com/qq_39940205/article/details/80539079

线程不安全的原因:


在多线程环境下,假设有容器map,其存储的情况如下图所示(淡蓝色为已有数据)。

è¿éåå¾çæè¿°

此时的map已经达到了扩容阈值12(16 * 0.75 = 12),而此时线程A与线程B同时对map容器进行插入操作,那么都需要扩容。此时可能出现的情况如下:线程A与线程B都进行了扩容,此时便有两个新的table,那么再赋值给原先的table变量时,便会出现其中一个newTable会被覆盖,假如线程B扩容的newTable覆盖了线程A扩容的newTable,并且是在A已经执行了插入操作之后,那么就会出现线程A的插入失效问题,也即是如下图中的两个table只能有一个会最后存在,而其中一个插入的值会被舍弃的问题。

è¿éåå¾çæè¿°

这便是HashMap的线程不安全性,当然这只是其中的一点。而要消除这种隐患,则可以加锁或使用HashTable和ConcurrentHashMap这样的线程安全类,但是HashTable不被建议使用,推荐使用ConcurrentHashMap容器。

那么怎么才能让HashMap变成线程安全的呢?

可以通过以下三种方法来实现:

1.替换成Hashtable,Hashtable通过对整个表上锁实现线程安全,因此效率比较低;(不推荐)

2.使用Collections类的synchronizedMap方法包装一下。方法如下:

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)  返回由指定映射支持的同步(线程安全的)映射;

3.使用ConcurrentHashMap,它使用分段锁来保证线程安全,效率高,推荐使用。

 

六、ConcurrentHashMap实现线程安全的原理

ConcurrentHashMap将数据分别放到多个Segment中,默认16个,每一个Segment中又包含了多个HashEntry列表数组,

对于一个key,需要经过三次hash操作,才能最终定位这个元素的位置,这三次hash分别为:

  1. 对于一个key,先进行一次hash操作,得到hash值h1,也即h1 = hash1(key);
  2. 将得到的h1的高几位进行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通过h2能够确定该元素的放在哪个Segment;
  3. 将得到的h1进行第三次hash,得到hash值h3,也即h3 = hash3(h1),通过h3能够确定该元素放置在哪个HashEntry。

每一个Segment都拥有一个锁,当进行写操作时,只需要锁定一个Segment,而其它Segment中的数据是可以访问的。

总结:ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),然后每次操作对一个segment加锁,避免多线程锁的几率,提高并发效率。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值