我们知道HashMap底层的数据结构是数组+链表/红黑树,默认初始容量是16,当数组中的元素大于hashMap的初始容量乘以加载因子时(加载因子默认是0.75),HashMap就会进行扩容,容量变为原来的2倍,然后将原来的数据重新映射到新的桶里面,然后将原来的桶逐个置为null,使得引用失效。也就是在hashMap进行扩容的时候容易发生HashMap线程不安全。HashMap线程不安全主要会产生以下两种情况:1、在多线程的环境下,进行put()的时候会导致多线程的数据不一致。2、在多线程的环境下,对HashMap进行get操作可能因为扩容而引起死循环。
1、在多线程的环境下,进行put()的时候会导致多线程的数据不一致。
例如有A、B两个线程,首先假设A获得CPU的执行权,A开始向HashMap中添加数据,先计算key的hash值,计算元素落到桶的索引坐标,然后获取到了桶里面的链表头结点,此时线程A的时间片用完了,线程B获得了cpu的执行权,线程B和线程A一样执行操作,只不过B成功的将元素添加到HashMap中了,此时线程A获得cpu执行权,假设A线程要插入的元素计算得到在桶中的索引和线程B插入元素的索引一样,线程B插入成功之后,线程A执行,此时线程A插入的数据会覆盖掉线程B插入的数据,这样线程B插入的记录就会凭空消失,造成了数据不一致。
2、在多线程的环境下,执行get()元素的时候可能会因为扩容引起死循环
HashMap是采用链表解决Hash冲突,因为是链表结构,那么就容易形成闭合链路,这样在循环的时候只要有线程对这个HashMap进行get操作就会产生死循环。在单线程的情况下,只有一个线程对HashMap数据结构进行操作,是不可能产生闭合回路的,当put的时候,如果size>initialCapacity*loadFactor,那么这时候HashMap就会进行rehash操作,随之HashMap的结构就会发生翻天覆地的变化。很有可能两个线程就是在这个时候同时出发rehash操作,产生了闭合回路。
如果想在多线程的环境下使用HashMap,该怎么办?
1、使用ConcurrentHashMap
2、使用Collections.synchonizedMap(Map<K,v> m)方法将HashMap变成一个线程安全的map。