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

目录

一、HashMap和Hashtable的区别

二、ArrayList、LinkedList、Vector的区别

三、HashMap和ConcurrentHashMap的区别

四、HashMap和LinkedHashMap的区别

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

六、ConcurrentHashMap是怎么实现线程安全的


一、HashMap和Hashtable的区别

 1、Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用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的指数。

二、ArrayList、LinkedList、Vector的区别

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

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

3、LinkedList的底层使用双向链表实现的,因此插入和删除的效率高。

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

三、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.csdn.net/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加锁,避免多线程锁的几率,提高并发效率。

猜你喜欢

转载自blog.csdn.net/qq_36145093/article/details/88343447