synchronizedMap 和 concurrenthashmap 分别适用于什么场景?
如何线程安全的使用HashMap
了解了 HashMap 为什么线程不安全,那现在看看如何线程安全的使用 HashMap。这个无非就是以下三种方式:
Hashtable
ConcurrentHashMap
Synchronized Map
常用的有Collections工具类的synchronizedMap创建的Map对象,是属于线程安全的;其次就是并发包下的ConcurrentHashMap类。
public synchronized V get(Object key) {
// 省略实现
}
public synchronized V put(K key, V value) {
// 省略实现
}
所以当一个线程访问 HashTable 的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用 put 方法时,另一个线程不但不可以使用 put 方法,连 get 方法都不可以.
ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长,但是带来的好处是写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上),所以,通过这一种结构,ConcurrentHashMap的并发能力可以大大的提高。
SynchronizedMap的put封装了HashMap的put方法,并加上互斥锁保证了安全性。JDK1.8的ConcurrentHashMap取消了segments字段,采用了transient volatile HashEntry<K,V>[] table保存数据。这样对每个table数组元素加锁,见源代码中synchronized(f),因此可以减少并发冲突的概率,提高并发性能。所以ConcurrentHashMap的put并发性更好,因此相同工作下ConcurrentHashMap花费时间更少。
对于get方法,SynchronizedMap同样封装了HashMap的get方法并且加了同步锁。
ConcurrentHashMap的get方法采用了与HashMap一样的思路,并没有加锁,所以性能上优于SynchrinizedMap的get方法。