参考:JDK1.7和JDK1.8中HashMap为什么是线程不安全的
(我理解的)
jdk1.7会发生死循环,数据丢失,数据覆盖这些问题,主要是因为扩容时是在transfer函数中完成数据迁移的,entry数组使用的是头插法(死循环,数据丢失)。
若两个线程同时对table进行扩容,线程A在在为桶赋值时时间片用完,此时线程B开始工作,知道扩容结束。这是old_Table已经更新为new_table,因为采用的是头插法,其实链表的链接顺序已发生改变,这是线程A继续工作,就会发生死循环,另外,若old_table中一个链表中的某些元素因为rehash被放到了其他的桶中,线程A是访问不到的,就会发生数据丢失。
jdk1.8 是在resize函数中完成了数据迁移,并且采用了尾插法,解决了死循环、数据丢失,但是仍会有数据覆盖的问题。
数据覆盖是因为hashmap在put()函数中,是先进行hash碰撞判断在插入元素的,若是两个线程A,B同时进行put操作,若线程A在hash碰撞之后、插入元素之前阻塞,接着线程B开始工作,正常完成了put操作,这是线程A接着插入元素,因为之前已经判断过hash碰撞了,所以直接插入元素,就会覆盖掉线程B正常插入的元素。
总结
HashMap的线程不安全主要体现在下面两个方面:
1.在JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况。
2.在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。