ConcurrentHashMap
是HashMap
的线程安全版本,内部也是使用数组、链表、红黑树的结构存储元素。
其继承关系如下:
![](/Java集合/ConcurrentHashMap/concurrentHashMap.png)
主要属性
1 | public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> |
内部类
1 | static class Node<K,V> implements Map.Entry<K,V> { |
构造器
1 | public ConcurrentHashMap() { |
添加元素
1 | public V put(K key, V value) { |
初始化桶数组
1 | private final Node<K,V>[] initTable() { |
判断是否需要扩容
每添加一个元素后 元素数量加1 并判断是否达到扩容门槛 达到则进行扩容或协助扩容1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
CounterCell a; long v; int m;
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
fullAddCount(x, uncontended);
return;
}
if (check <= 1)
return;
s = sumCount();
}
if (check >= 0) {
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n);
if (sc < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);
s = sumCount();
}
}
}
协助扩容
1 | final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) { |
迁移元素
1 | private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) { |
删除元素
1 | public V remove(Object key) { |
获取元素
1 | public V get(Object key) { |
获取元素个数
1 | public int size() { |
总结
ConcurrentHashMap
使用到的技术点:
- CAS+自旋 乐观锁的思想减少线程上下文切换
- 分段锁的思想 减少同一把锁争用带来的低效问题
- CounterCell 分段存储元素个数 减少多线程同时更新一个字段带来的低效
- @sun.misc.Contended(CounterCell上的注解) 避免伪共享
- 多线程协同进行扩容