HashMap在1.7 1.8中的线程安全问题

HashMap的线程不安全主要体现在下面两个方面:

在JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况。
在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。

 
 

常被问到的HashMap和Hashtable的区别

1、线程安全
两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全。
Hashtable的实现方法里面都添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些,我们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合。

Note:Collections.synchronizedMap()实现原理是Collections定义了一个SynchronizedMap的内部类,这个类实现了Map接口,在调用方法时使用synchronized来保证线程同步,当然了实际上操作的还是我们传入的HashMap实例,简单的说就是Collections.synchronizedMap()方法帮我们在操作HashMap时自动添加了synchronized来实现线程同步,类似的其它Collections.synchronizedXX方法也是类似原理。

2、针对null的不同
HashMap可以使用null作为key,而Hashtable则不允许null作为key
虽说HashMap支持null值作为key,不过建议还是尽量避免这样使用,因为一旦不小心使用了,若因此引发一些问题,排查起来很是费事。
Note:HashMap以null作为key时,总是存储在table数组的第一个节点上。

3、继承结构
HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类。

4、初始容量与扩容
HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。

HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1。

5、两者计算hash的方法不同
Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

 
 

怎么解决这个线程不安全的问题

  • Java中有HashTable、Collections.synchronizedMap、以及ConcurrentHashMap可以实现线程安全的Map。

  • HashTable是直接在操作方法上加synchronized关键字,锁住整个数组,粒度比较大,

  • Collections.synchronizedMap是使用Collections集合工具的内部类,通过传入Map封装出一个SynchronizedMap对象,内部定义了一个对象锁,方法内通过对象锁实现

  • ConcurrentHashMap使用分段锁,降低了锁粒度,让并发度大大提高。
     
     

为啥Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null?

因为hashtable,concurrenthashmap它们是用于多线程的,并发的 ,如果map.get(key)得到了null,不能判断到底是映射的value是null,还是因为没有找到对应的key而为空

翻译一下最后一句话,其实contains(key)也是可以使用synchronized进行同步的,所以调用contains(key)得到的就是该线程本时刻map的状态,本身不存在歧义。问题的关键在于多线程情况下,即便此刻你通过contains(key)知晓了是否包含null,下一步当你使用这个结果去做一些事情时可能其他线程已经改变了这种状态,这在单线程下是不可能发生的。

这是因为Hashtable使用的是安全失败机制(fail-safe),这种机制会使你此次读到的数据不一定是最新的数据。
如果你使用null值,就会使得其无法判断对应的key是不存在还是为空,因为你无法再调用一次contain(key)来对key是否存在进行判断(意思是contains不可靠,看上面那一段),ConcurrentHashMap同理。

资料













种一棵树最好的时间是十年前,其次是现在。
原文地址:https://www.cnblogs.com/islch/p/12821753.html