我们知道HashMap是线程不安全的,那么当我们要在多线程的情况下,应该怎么办呢?
在多线程场景下,我们一般采用下面的几种方式去创建线程安全的map集合
通常会采用ConcurrentHashMap,因为前面两者的并发度太低了。
synchronizedMap是Collections工具类中的一个内部类。
从下面的源码中可以看出来,SynchronizedMap维护了一个Map和一个互斥锁
private static class SynchronizedMap<K,V>
implements Map<K,V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
// Map对象
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;
}
.................
................
}
可以看到有两个构造器,需要传入一个Map对象,如果没有传入互斥锁对象,那么互斥锁会赋值为this
创建好之后对map的各种操作都是带锁的,??
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);}}
所以并发度很低,每次只允许一个线程对数据进行操作
Hashtable和HashMap的结构差不多。
Hashtable是线程安全的原因是因为,他在操作数据
的方法上都加了synchronized
,同样的,这也导致他的效率非常低。
public synchronized int size() {
return count;
}
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
.......
.......
}
Hashtable | HashMap |
---|---|
线程安全 | 线程不安全 |
继承Dictionary | 继承AbstractMap |
不允许key或者value为null | 允许为null |
数组默认大小为11,扩容为2*old+1 | 数组默认大小为16 , 扩容为2*old |
其实现在多线程环境下一般也不用Hashtable,他是一个遗留类,内部实现很多没有优化
一般开发中用的都是ConcurrentHashMap,并发度高,效率高。
同样的,ConcurrentHashMap的键值不允许为null
1.7的时候采用的是分段锁的方式。(给每个段(Segment)用lock锁进行保护,相对Hashtable的synchronized关键字锁粒度更细,并发度更高)
1.8的时候优化成CAS+synchronized的方式。
具体详见??
ConcurrentHashMap源码分析
原文:https://www.cnblogs.com/bendandedaima/p/13546856.html