hashmap 线程不安全原理,底层实现

jdk1.7与jdk1.8中HashMap区别

JDK7,HashMap的结构很简单,基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储。这样子的HashMap性能上就有问题了,如果有成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,就会花很长的时间,从而导致性能损失。

JDK8中采用的数组+链表+红黑树结构的方式,也是非线程安全的。当某个位桶的链表的长度达到某个阀值的时候,这个链表就将转换成红黑树。

插入键值对的put方法的区别,1.8中会将节点插入到链表尾部,而1.7中是采用头插。

 扩容策略:1.7中是只要不小于阈值就直接扩容2倍;而1.8的扩容策略会更优化,当数组容量未达到64时,以2倍进行扩容,超过64之后若桶中元素个数不小于7就将链表转换为红黑树,但如果红黑树中的元素个数小于6就会还原为链表,当红黑树中元素不小于32的时候才会再次扩容。

之所以链表要转成红黑树,还是为了解决存取效率的问题。链表过长,取数据的效率就很慢,红黑树插入比较慢,但取数据还是很快的。

答案:

正常情况下  hashmap 在保存数据时,底层是数组+链表+红黑树  但是 你去源码中看时,发现子啊hashMap 底层没有加任何的多线程中的锁机制,比如: synchronize修饰  ,所以在多线程的情况下  hashMap 的单项链表,可能会变成一个环形的链表,所以这个链表上的Next元素的指向永远不为null, 所以在遍历的时候就是死循环啊。

HashMap是线程不安全的,其主要体现:

#1.在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。

#2.在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。

1HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的

2 HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。在hashmap做put操作的时候会调用到以上的方法。现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失

ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个,然后每次操作对一个segment加锁,避免多线程锁的几率,提高并发效率

你可能感兴趣的:(hashmap 线程不安全原理,底层实现)