HashMap 是非线程安全的。在多线程条件下,容易致使死循环,具体表现为CPU使用率100%。所以多线程环境下保证 HashMap 的线程安全性,主要有以下几种方法:java
(一)java.util.Hashtable类:数组
查看该类的源码安全
public synchronized V get(Object key) { …… //具体的实现省略,请参考 jdk实现 } public synchronized V put(K key, V value) { …… //具体的实现省略,请参考 jdk实现 } public synchronized V remove(Object key) { …… //具体的实现省略,请参考 jdk实现 }
上面是 Hashtable 类提供的几个主要方法,包括 get(),put(),remove() 等。注意到每一个方法自己都是 synchronized 的,不会出现两个线程同时对数据进行操做的状况,所以保证了线程安全性,可是也大大的下降了执行效率。所以是不推荐的。数据结构
(二)使用 java.util.concurrent.ConcurrentHashMap 类:多线程
该类是 HashMap 的线程安全版,与 Hashtable 相比, ConcurrentHashMap 不只保证了访问的线程安全性,并且在效率上有较大的提升。并发
ConcurrentHashMap的数据结构以下:spa
能够看出,相对 HashMap 和 Hashtable, ConcurrentHashMap 增长了Segment 层,每一个Segment 原理上等同于一个 Hashtable, ConcurrentHashMap 等同于一个 Segment 的数组。下面是 ConcurrentHashMap 的 put 和 get 方法:线程
final Segment<K,V> segmentFor(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; } public V put(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false); } public V get(Object key) { int hash = hash(key.hashCode()); return segmentFor(hash).get(key, hash); }
向 ConcurrentHashMap 中插入数据(put) 或者 读取数据(get),首先都要将相应的 Key 映射到对应的 Segment,所以不用锁定整个类, 只要对单个的 Segment 操做进行上锁操做就能够了。理论上若是有 n 个 Segment,那么最多能够同时支持 n 个线程的并发访问,从而大大提升了并发访问的效率。code