首先须要明确的是,无论使用那种Map,都不能保证公共混合调用的线程安全,只能保证单条操做的线程安全,在这一点上各Map不存在优劣。html
前文中简单说过HashTable和synchronizedMap,其实这两个类不须要说太多,把代码贴一下相信看过Java多线程的就能很容易理解了。java
HashTable的话,实现这个样子的。能够看到的是,对于Hash表的全部操做,HashTable都加了锁,但也只能保证单条操做的线程安全。数组
public synchronized V get(Object key) { // 省略实现 } public synchronized V put(K key, V value) { // 省略实现 }
synchronizedMap的实现以下,没直接在方法上加,尽管其实质与HashTable是等效的,也一样有HashTable的缺陷,但synchronizedMap给用户留下了选择的空间:用户能够在不须要加锁时直接操做原始Map,在实际编码时就能够基于这点进行优化。安全
// 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; 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);} } // 省略其余方法 }
提升安全HashMap的并发性的方法,能够经过减少锁粒度的方式,不对整个Hash表加锁,而是对每一个bucket加锁,甚至用锁池,每一个锁维护几个bucket,让Map的不一样部分能够被多个线程访问,不过这样的方式会让对总体集合操做的方法的实现更加困难。Java7中的ConcurrentHashMap就经过Segment引入了这个分段加锁概念,但Java8因为上述困难更改了机制,引入了红黑树结构,去掉了Segment。数据结构
JDK1.8的改进后,ConcurrentHashMap的写性能有10%左右的下降,但读性能有了很大提高。主要是将过于集中的hash节点的效率从O(N)提升到了O(LOGN)。多线程
ConcurrentHashMap利用了CAS进行实现,从而以乐观锁的方式实现了线程安全的HashMap,concurrentHashMap的源码很复杂,一些方法的实现思路以下:并发
Java8的ConcurrentHashMap的数据结构实现思路大概为,对于Hash表中每个节点,其数据结构能够为单节点,链表数组或红黑树,随着节点中元素增长而改变。(改变方法见treeifyBin)。性能
该方法用于对数组链表扩容,或将链表结构转化为红黑树,一个节点的元素个数大于链表阈值(默认8)时,若是数组链表长度小于红黑树阈值(默认64),则对数组链表扩容,不然将该节点转换为红黑树。优化
这些方法负责hash表扩容,因为要经过CAS实现线程安全,代码十分复杂。大概思路为,原数组长度为n,则产生n个迁移任务,让每个线程负责一个小任务,以后监测是否有其余没作完的任务,帮助迁移。ui
get方法不涉及CAS操做,实现较为简单,计算hash值,找到对应节点进行判断:
目前多线程环境下ConcurrentMap的性能有很高的优越性,一般状况下,若是你的Map处于多读少写的场景,优先考虑ConcurrentMap,但在多写少读的情境中,因为资源竞争激烈,CAS自旋可能致使ConcurrentMap性能不如synchronizedMap。
Collections.synchronizedMap()、ConcurrentHashMap、Hashtable之间的区别
Java8 ConcurrentHashMap详解
浅谈Java8中的ConcurrentHashMap
SynchronizedMap