实现线程安全的方式有三种,分别是使用HashTable、Collections.SynchronizeMap、ConcurrentHashMap。
一、HashTable
Hashtable 源码中是使用 synchronized 来保证线程安全的,比如下面的 get 方法和 put 等方法:
public synchronized V put(K key, V value) { ... }
public synchronized V get(Object key) { ... }
public synchronized int size(Object key) { ... }
public synchronized boolean isEmpty() { ... }
public synchronized V remove(Object key) { ... }
public synchronized void putAll(Map<? extends K, ? extends V> t) { ... }
...
所以当一个线程访问 HashTable 的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。因此Hashtable效率很低,基本被废弃。。
二、SynchronizedMap
SynchronizedMap是Collectionis的内部类
//使用方法
Map m = Collections.synchronizedMap(new HashMap());
//源码部分
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
/**
* @serial include
*/
private static class SynchronizedMap<K,V>
implements Map<K,V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
private final Map<K,V> m; // Backing Map
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();}
}
public boolean containsKey(Object key) {
synchronized (mutex) {return m.containsKey(key);}
}
public boolean containsValue(Object value) {
synchronized (mutex) {return m.containsValue(value);}
}
public V get(Object key) {
synchronized (mutex) {return m.get(key);}
}
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
public V remove(Object key) {
synchronized (mutex) {return m.remove(key);}
}
public void putAll(Map<? extends K, ? extends V> map) {
synchronized (mutex) {m.putAll(map);}
}
public void clear() {
synchronized (mutex) {m.clear();}
}
private transient Set<K> keySet;
private transient Set<Map.Entry<K,V>> entrySet;
private transient Collection<V> values;
Collections.synchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步。效率还是硬伤。
三、ConcurrentHashMap
ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问,极大提升了效率。相关源码敬请期待!
四、三者的效率对比
分别通过三种方式创建 Map 对象,使用 ExecutorService 来并发运行5个线程,每个线程添加/获取500K个元素,比较其用时多少。
System.out: Test started for: class java.util.Hashtable
System.out: 500K entried added/retrieved in 92962 ms
System.out: 500K entried added/retrieved in 92199 ms
System.out: 500K entried added/retrieved in 92549 ms
System.out: 500K entried added/retrieved in 93331 ms
System.out: 500K entried added/retrieved in 92387 ms
System.out: For class java.util.Hashtable the average time is 92685 ms
System.out: Test started for: class java.util.Collections$SynchronizedMap
System.out: 500K entried added/retrieved in 99217 ms
System.out: 500K entried added/retrieved in 99487 ms
System.out: 500K entried added/retrieved in 100437 ms
System.out: 500K entried added/retrieved in 99606 ms
System.out: 500K entried added/retrieved in 100804 ms
System.out: For class java.util.Collections$SynchronizedMap the average time is 99910 ms
System.out: Test started for: class java.util.concurrent.ConcurrentHashMap
System.out: 500K entried added/retrieved in 10360 ms
System.out: 500K entried added/retrieved in 9359 ms
System.out: 500K entried added/retrieved in 8621 ms
System.out: 500K entried added/retrieved in 9833 ms
System.out: 500K entried added/retrieved in 9964 ms
System.out: For class java.util.concurrent.ConcurrentHashMap the average time is 9627 ms
网友评论