一、定义
CocurrentHashMap是线程安全版的HashMap,可以实现线程安全的集合并发操作。在jdk7版本,CocurrentHashMap的底层数据结构是基于数组+链表,且采用分段锁的机制保证线程安全。在jdk8版本,Cocurrent的底层数据结构是基于数组+链表+红黑树,且采用CAS+synchronized的机制来保证线程安全。
二、特性
三、原理
1、保证线程安全的机制
(1)JDK7版本:使用分段锁
CocurrentHashMap会有一个Segment数组,存储Segment元素,每个Segment元素存储若干个HashEntry对象(CocurrentHashMap存储的元素)。其中,每个Segment是一个可重入锁,当出现锁竞争时候,会锁住对应的Segment,从而提高了并发性能。
put方法调用过程(具备线程安全):
(2)JDK8版本:使用CAS+synchronized机制
CocurrentHashMap在进行put操作添加元素时,会先通过CAS机制判断哈希表对应下标的值是否存在,若为空,则使用CAS机制把哈希表对应key的值设置为value;若哈希表对应下标已经有值,则会锁住哈希冲突节点,然后进行添加元素操作,会先判断当前节点是链表节点还是树节点,若是链表节点则会先把该元素放在链表中,然后判断当前链表长度是否大于等于8,是的话会进行树化,在树化过程中,会再判断当前哈希表容量是否大于等于64,是的话,则会把链表转为红黑树,否则,则会调用扩容机制;若是树节点则会通过红黑树的方式加入,该过程和HashMap添加元素的过程一致。
2、添加元素put方法的调用过程(具备线程安全)
- put(K key, V value)
- putVal(key, value, false)
- putVal(K key, V value, boolean onlyIfAbsent)
- if (key == null || value == null) throw new NullPointerException();(判断key或value是否为空)
- int hash = spread(key.hashCode());
- int binCount = 0
- for (Node<K,V>[] tab = table;;)(遍历哈希表)
- Node<K,V> f; int n, i, fh
- if (tab == null || (n = tab.length) == 0)(判断哈希表是否为null或者长度为0)
- tab = initTable()(是,进行初始化哈希表)
- f = tabAt(tab, i = (n - 1) & hash)(获取哈希表对应下标的元素)
- tabAt(Node<K,V>[] tab, int i)
- Unsafe getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE)
- tabAt(Node<K,V>[] tab, int i)
- else if ((f) == null)(判断哈希表对应下标是否有元素)
- boolean flag=casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null))(该位置没有元素,则通过CAS方式修改该位置的值为新元素)
- casTabAt(Node<K,V>[] tab, int i,Node<K,V> c, Node<K,V> v)
- Unsafe compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v)
- casTabAt(Node<K,V>[] tab, int i,Node<K,V> c, Node<K,V> v)
- if (flag)(插入元素成功)
- break
- boolean flag=casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null))(该位置没有元素,则通过CAS方式修改该位置的值为新元素)
- else if ((fh = f.hash) == MOVED)(判断是否需要扩容)
- tab = helpTransfer(tab, f)
- else
- V oldVal = null
- synchronized (f)(锁住发生哈希冲突的节点)
- if (tabAt(tab, i) == f)
- if (fh >= 0)(f为链表节点)
- binCount = 1
- for (Node<K,V> e = f;; ++binCount)
- K ek
- if (e.hash == hash &&((ek = e.key) == key ||(ek != null && key.equals(ek))))
- oldVal = e.val
- if (!onlyIfAbsent)(判断是否允许覆盖)
- e.val = value(是,则更新e的值)
- break
- Node<K,V> pred = e
- if ((e = e.next) == null)
- pred.next = new Node<K,V>(hash, key,value, null)
- break
- else if (f instanceof TreeBin)(f为树节点)
- Node<K,V> p
- binCount = 2
- if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,value)) != null)
- oldVal = p.val
- if (!onlyIfAbsent)
- p.val = value
- if (fh >= 0)(f为链表节点)
- if (tabAt(tab, i) == f)
- if (binCount != 0)
- if (binCount >= TREEIFY_THRESHOLD)(判断是否需要树化)
- treeifyBin(tab, i)
- if (oldVal != null)
- return oldVal
- break
- if (binCount >= TREEIFY_THRESHOLD)(判断是否需要树化)
- addCount(1L, binCount)
- return null
- putVal(K key, V value, boolean onlyIfAbsent)
- putVal(key, value, false)