HashMap 是线程安全的吗?多线程下会有什么问题?
HashMap不是线程安全的,多线程下可能会发生这些问题:
1.多线程下扩容死循环。JDK 1.7 中的 HashMap 使用头插法插入元素,在多线程的环境下,扩容的时候有可能导致环形链表的出现,形成死循环。因此,JDK 1.8 使用尾插法插入元素,在扩容时会保持链表元素原本的顺序,不会出现环形链表的问题。
2.多线程的 put 可能导致元素的丢失。多线程同时执行 put 操作,如果计算出来的索引位置是相同的,那会造成前一个 key 被后一个 key 覆盖,从而导致元素的丢失。此问题在 JDK 1.7 和 JDK 1.8 中都存在。
3.put 和 get 并发时,可能导致 get 为 null。线程 1 执行 put 时,因为元素个数超出 threshold 而导致 rehash,线程 2 此时执行 get,有可能导致这个问题。这个问题在 JDK 1.7 和 JDK 1.8 中都存在。
有什么办法能解决HashMap线程不安全的问题呢?
Java 中有 HashTable、Collections.synchronizedMap、以及 ConcurrentHashMap 可以实现线程安全的 Map。
HashTable 是直接在操作方法上加 synchronized 关键字,锁住整个 table 数组,粒度比较大;
Collections.synchronizedMap 是使用 Collections 集合工具的内部类,通过传入 Map 封装出一个 SynchronizedMap 对象,内部定义了一个对象锁,方法内通过对象锁实现;
ConcurrentHashMap 在 JDK 1.7 中使用分段锁,在 JDK 1.8 中使用 CAS + synchronized。
ConcurrentHashmap 的实现
ConcurrentHashmap 线程安全在 JDK 1.7 版本是基于分段锁实现的,在 JDK 1.8 是基于 CAS + synchronized 实现。
分段锁和 CAS + synchronized 的具体内容后续补充......
本文参考自:面渣逆袭:HashMap追魂二十三问 - 掘金