为什么HashMap不是线程安全的?

373 阅读1分钟

1.JDK1.7中HashMap多线程插入出现的死循环问题

HashMap的容量是有限的,多次元素插入后会使得HashMap达到一定饱和度,Key映射位置发生hash冲突的几率会提高。此时,HashMap需要扩展它的长度,也就是进行resize操作。

衡量HashMap是否需要进行resize的条件为HashMap.size() >= Capacity * LoadFactor,其中Capacity为原来的HashMap的长度,LoadFactor负载因子(默认为0.75)。

HashMap经过resize扩容会创建一个新的Entry空数组,长度是原数组的2倍(初始状态下HashMap容量为16)。扩容后HashMap还需要进行rehash操作hash公式为index = hashcode(key)& (length - 1)

resize之前的HashMap: image.png

resize之后的HashMap: image.png

在多线程情况下: 假设一个HashMap已经到了resize的临界点。此时有两个线程A和B,在同一时刻对HashMap进行put操作,由于Java7中采用头插法解决hash冲突,在rehash过程中,A、B两个线程在可能会形成链表环,造成死循环。

image.png

2.为什么JDK8中的HashMap仍是线程不安全的?

在JDK8中,采用拉链法解决hash冲突时,尽管采用尾插法代替头插法,但是使用HashMap并不只是仅仅用来插入数据,诚然1.8解决了死链问题,但是在多线程情况下还是有可能修改HashMap。比如多个线程同时修改HashMap中的某个Key的值,先前的那个线程修改的值可能被之后的线程修改覆盖掉,那么HashMap就不是线程安全了。
所以在Java8中HashMap只是在多线程下插入数据安全,但是修改并不线程安全的