CurrentHashMap源码学习笔记
由于
HashMap
是线程不安全的在多线程并发下容易出现问题,比如自身的死锁问题,而HashTable
虽然是线程安全的,但是由于它的内部是通过对所有操作方法进行方法级别的synchronized
加锁,导致性能太低(synchronized
太过重量级),所以引入CurrentHashMap
jdk1.7
采用segment分段枷锁机制来控制并发
segment[] 分段锁数组
segment – 多个hashMap –> HashEntry
多个小的HashMap公用同一把锁
put时先确定放到那个segment中然后再确定放到改segment中的那个数组中剩下的和jdk7中的hashMap一样
jdk1.8
基于
CAS
的乐观锁实现来控制并发思路:
对于数组上的链表 任何操作都需要通过拿到头节点才能进行后续操作,所以只对头节点进行加锁效率更高源码分析put
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68final V putVal(K key, V value, boolean onlyIfAbsent) {
//key不能为null
if (key == null || value == null) throw new NullPointerException();
//进行hash计算
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//table为空则初始化
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//table不为空 但是该位置元素为空
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
// 加锁 f就是当前数组链表的头节点 *重点*
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
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;
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) {
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 (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}