Java 非线程安全的HashMap如何在多线程中使用

Java 非线程安全的HashMap如何在多线程中使用

 

HashMap 是非线程安全的。在多线程条件下,容易致使死循环,具体表现为CPU使用率100%。所以多线程环境下保证 HashMap 的线程安全性,主要有以下几种方法:java

  1. 使用 java.util.Hashtable 类,此类是线程安全的。
  2. 使用 java.util.concurrent.ConcurrentHashMap,此类是线程安全的。
  3. 使用 java.util.Collections.synchronizedMap() 方法包装 HashMap object,获得线程安全的Map,并在此Map上进行操做。
  4. 本身在程序的关键代码段加锁,保证多线程安全(不推荐)

 

接下来分析上面列举的几种方法实现并发安全的 HashMap 的原理:

(一)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

相关文章
相关标签/搜索