你知道为什么HashMap是线程不安全的吗?
我们都知道HashMap是线程不安全的,在多线程环境中不建议使用,但是其线程不安全主要体现在什么地方呢?
jdk1.7中的HashMap
扩容造成死循环
根源在transfer函数。
在对table进行扩容到newTable后,需要将原来数据转移到newTable中,在转移元素的过程中,使用的是头插法,也就是链表的顺序会翻转,这里有机会形成环形列表,是形成死循环的关键点。
扩容造成数据丢失
出现数据覆盖,造成数据丢失。
jdk1.8中HashMap
在jdk1.8中对HashMap进行了优化,在发生hash碰撞,不再采用头插法方式,而是直接插入链表尾部,因此不会出现环形链表的情况,但是在多线程的情况下仍然不安全。
put操作出现数据覆盖
在put操作的主函数中,如果没有hash碰撞则会直接插入元素。如果线程A和线程B同时进行put操作,刚好这两条不同的数据hash值一样,并且该位置数据为null,所以这线程A、B都会进入代码中。
假设一种情况,线程A进入后还未进行数据插入时挂起,而线程B正常执行,从而正常插入数据,然后线程A获取CPU时间片,此时线程A不用再进行hash判断了,问题出现:线程A会把线程B插入的数据给覆盖,发生线程不安全。
总结
首先HashMap是线程不安全的,其主要体现:
- 在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。
- 在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
参考: |