Map、list 线程安全问题
什么叫做线程安全?
单线程和多线程执行的结果总是唯一的。
一、HashMap解析
HashMap是线程不安全的,多线程情况下不推荐使用HashMap。它的key,value运行为null
二、Hashtable解析
Hashtable在jdk1.1就有了,那么它是怎样实现线程安全的呢?主要看put、remove、get方法猜它肯定进行的同步控制的。于是看源码:
//get它搞成了同步方法,保证了get的安全性
public synchronized V get(Object key) {
……
}
//synchronized,同样
public synchronized V put(K key, V value) {
……
}
//也是搞成了同步方法
public synchronized V remove(Object key) {
……
}
所以为什么Hashtable是线程安全的,因为它的remove,put,get做成了同步方法,保证了Hashtable的线程安全性。
每个操作数据的方法都进行同步控制之后,由此带来的问题任何一个时刻只能有一个线程可以操纵Hashtable,所以其效率比较低。
三、Collections.synchronizedMap()解析
我们看源码:
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();}
}
我们惊人的发现,synchronizedMap只是将HashMap的操纵放在了同步代码块中来保证SynchronizedMap的线程安全性。因此,SynchronizedMap也可以允许key和value为null。这样带来的问题也是任何一个时刻只能有一个线程可以操纵synchronizedMap,所以其效率比较低。
同样,Collections下的SynchronizedXX也是用同样方法实现线程安全性的。如SynchronizedSortedMap
四、ConcurrentHashMap
为什么ConcurrentHashMap可以多线程访问呢?是因为ConcurrentHashMap将Map分段了,每个段进行加锁,而不是想Hashtable,SynchronizedMap是整个map加锁,这样就可以多线程访问了。
如图:
ConcurrentHashMap默认运行16个线程同时访问该map。但是我们可以通过一个函数来设置增加或减少最大可运行访问的线程数目。
//就是这个concurrencyLevel
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel)
所以就有了下面这几个问题:
(1) 多线程可以同时写一个ConcurrentHashMap的分段吗?
不行。分段就像一个单独的HashMap,只允许一个线程向其写入数据
(2)多个线程可以同时写入不同分段吗?
这当然可以咯!
(3)多个线程可以同时从一个分段中读数据吗?
可以
(4)如果一个线程正在向一个分段写入数据,其他线程可以从该分段中读取数据吗?
可以。但是读取到最后更新的数据。
最后需要注意的一点是CoucrrentHashMap是不允许key和vlaue为null的。
六、ArrayList为什么是线程不安全的,Vector是线程安全的?
Vector肯定是做了同步控制了的。看源码
//同步方法
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
//同步方法
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
所以Vector也是用同步方法的方式来保证线程安全的。
参考文献
https://codepumpkin.com/hashtable-vs-synchronizedmap-vs-concurrenthashmap/
hashMap 无序
treeMap 按照数字从小到大的顺序 或者字母顺序,先入不一定先出。
linkedhashMap 按照放入的顺序,先入先出。