Java 基础 HashMap

深究Java基础

Posted by lichao modified on May 12, 2020
  1. 在JDK 1.7 中,当并发执行扩容操作时会造成环形链和数据丢失的情况。

  2. 在JDK 1.8 中,在并发执行put操作时会发生数据覆盖的情况。

比如有两个线程A和B,首先 A 希望插入一个 key-value 对到HashMap中,首先计算记录所要落到的 hash桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的 hash桶索引和线程B要插入的记录计算出来的 hash桶索引是一样的,那么当线程 B 成功插入之后,线程 A 再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。

HashMap 的设计目标是简洁高效,没有采取任何措施保证 put、remove 操作的多线程安全。下面这张图是 JDK 1.8 的 HashMap 的 put 方法的操作逻辑,可以发现 put 方面的操作对象要么是整个散列表,要么是某个哈希桶里的链表或红黑树,而这些过程都没有采取措施保证多线程安全。在这个复杂的逻辑过程中,任何一个线程在这个过程中改动了散列表的结构,都有可能造成另一个线程的操作失败。

java