SynchronizedMapHashtableConcurrentHashMap
HashMap不是线程安全的,即在并发场景下,多个线程共享一个HashMap对象,将会出现程序紊乱。在并发场景下如果要保证一种可行的方式是使用 Collections.synchronizedMap() 方法来包装我们的 HashMap。
/**
* Collections.synchronizedMap()源码,返回一个SynchronizedMap对象
*/
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
/**
* SynchronizedMap源码
*/
private static class SynchronizedMap<K,V>
implements Map<K,V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
/**
* SynchronizedMap内部依靠Map实现常见的K-V操作。
*/
private final Map<K,V> m; // Backing Map
/**
* 锁对象,用于控制SynchronizedMap内部的Map对象m的线程安全。
*/
final Object mutex; // Object on which to synchronize
SynchronizedMap(Map<K,V> m) {
this.m = Objects.requireNonNull(m);
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
this.mutex = mutex;
}
public int size() {
synchronized (mutex) {return m.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return m.isEmpty();}
}
······
SynchronizedMap示意图
与SynchronizedMap类似,Hashtable也是通过synchronized实现线程安全的,不同的是,Hashtable并没有借助内部的属性作为锁对象,而是在每个方法上加上了synchronized关键字,即Hashtable使用的锁对象是Hashtable对象本身。
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
/**
* The hash table data.
*/
private transient Entry<?,?>[] table;
/**
* The total number of entries in the hash table.
*/
private transient int count;
/**
* The table is rehashed when its size exceeds this threshold. (The
* value of this field is (int)(capacity * loadFactor).)
*
* @serial
*/
private int threshold;
/**
* Returns the number of keys in this hashtable.
*
* @return the number of keys in this hashtable.
*/
public synchronized int size() {
return count;
}
/**
* Tests if this hashtable maps no keys to values.
*
* @return <code>true</code> if this hashtable maps no keys to values;
* <code>false</code> otherwise.
*/
public synchronized boolean isEmpty() {
return count == 0;
}
······
SynchronizedMap和Hashtable都是通过使用一个全局的锁来同步不同线程间的并发访问,因此会带来不可忽视的性能问题——锁的粒度是方法级的,因此同一个方法无法在多线程环境下并发执行。于是就有了 HashMap 的线程安全版本—— ConcurrentHashMap 的诞生。在 ConcurrentHashMap 中,无论是读操作还是写操作都能保证很高的性能:在进行读操作时(几乎)不需要加锁,而在写操作时,JDK1.7和JDK1.8分别使用了不同的方式实现了线程安全。
JDK 1.7 ConcurrentHashMap
JDK 1.8 ConcurrentHashMap