ConcurrentHashMap详解

注:该文章主要讲的是JDK1.6中ConcurrentHashMap的实现,JDK1.8中ConcurrentHashMap的实现由不同的机制,详解可看:ConcurrentHashMap总结

概述

ConcurrentHashMap的特点:

  • ConcurrentHashMap内部使用了分段锁+哈希数组+链表的实现(JDK1.6)
  • ConcurrentHashMap是遍历无序、线程安全
  • ConcurrentHashMap的迭代器不是快速失败的,也就是说我们在遍历的时候可以对ConcurrentHashMap的结构进行修改,而不会抛出 ConcurrentModificationException异常
  • HashMap中key不能有重复元素,并且key与value都不能为null

实现机制

锁分段技术(分拆锁技术)

《Java并发编程艺术》

HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一>部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞>>争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段>技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

《Java并发编程实践》

分拆锁(lock spliting)就是若原先的程序中多处逻辑都采用同一个锁,但各个逻辑之间又相互独立,就可以拆(Spliting)为使用多个锁,每个锁守护不同的逻辑。 分拆锁有时候可以被扩展,分成可大可小加锁块的集合,并且它们归属于相互独立的对象,这样的情况就是分离锁(lock striping)。

ConcurrentHashMap结构

ConcurrentHashMap的结构与HashMap的结构类似,只是在中间加了一层锁(Segment)。其类图如下:

ConcurrentHashMap是有Segment数组组成,每个Segment由HashEntry数组组成,每个HashEntry数组中存放的是HashEntry链表。其中Segment是可重入锁ReentrantLock,它代表锁结构,每个Segment守护它自己包含的HashEntry数组中的元素,这样就体现出锁分段技术。我们在并发访问时,该Segment锁仅仅锁住它自己的一部分数据,从而不会影响其他部分的数据的读写。其结构图如下:

源码分析

自己在分析时,有许多的方法都没有分析,主要分析了几个方法的逻辑,其他方法类似。下面的类来自JDK1.6,自己删除了一些字段与方法。

HashEntry类

  • HashEntry用来封装key、value
  • key,hash 和 next 域都被声明为 final 型,至于为何为 final变量,后面会分析
  • value 域被声明为 volatile 型,至于为何为 volatile 变量,后面会分析
    注:next申明为final类型,说明HashEntry链表只能从头插入,而且不能删除。
1
2
3
4
5
6
7
8
9
10
11
12
13
static final class HashEntry<K,V> {
final K key;
final int hash;
volatile V value; // value 声明为 volatile 类型
final HashEntry<K,V> next; // next 申明为 final 类型

HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
this.key = key;
this.hash = hash;
this.next = next;
this.value = value;
}
}

Segment类

  • Segment 类继承 ReentrantLock,用来守护其含有的数据
  • count 代表该 Segment 中 HashEnty 的数量,是 volatile 类型。
  • modCount 代表该 Segment 结构改变的次数
  • loadFactor 代表负载因子;threshold 代表该 Segment 最大容量(这两个字段与HashMap中意义相似,用来扩容的)
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
static final class Segment<K,V> extends ReentrantLock implements Serializable { 
/**
* 该 Segment 中包含的 HashEntry 元素的个数
* 该变量被声明为 volatile 型
*/
transient volatile int count;
/**
* 该 Segment 结构改变的次数
*/
transient int modCount;
/**
* 该 Segment 的最大容量
*/
transient int threshold;
/**
* table 是由 HashEntry 数组,用来存放HashEnty链表
*/
transient volatile HashEntry<K,V>[] table;
/**
* 加载因子
*/
final float loadFactor;
/**
* 根据 key 的散列值,找到该散列值对于的 HashEntry 链表
*/
HashEntry<K,V> getFirst(int hash) {
HashEntry<K,V>[] tab = table;
return tab[hash & (tab.length - 1)];
}
/**
* 初始化 HashEntry 数组,设置加载因子 loadFactor
* 计算最大容量 = HashEntry 数组长度 * 加载因子 loadFactor
*/
Segment(int initialCapacity, float lf) {
...
}
/**
* 下面以下方法主要是实现Map的操作的方法,如put、get等等,
* 方法没有写完,后面会详细分析几个方法
* ConcurrentHashMap 对数据的操作其实主要是在 Segment 中对数据
* 操作的体现。
*/
/* Specialized implementations of map methods */
V get(Object key, int hash) {
...
}
boolean containsKey(Object key, int hash) {
...
}
}

ConcurrentHashMap类

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
public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 
implements ConcurrentMap<K, V>, Serializable {
/**
* 由 Segment 对象组成的数组
*/
final Segment<K,V>[] segments;
/**
* 该方法会初始化默认初始容量 (16)、默认加载因子 (0.75) 和 默认并发级别 (16)
* 的空散列映射表。主要功能就是初始化 Segment 数组
*/
public ConcurrentHashMap() {
...
}
/**
* 散列算法
*/
private static int hash(int h) {
// Spread bits to regularize both segment and index locations,
// using variant of single-word Wang/Jenkins hash.
h += (h << 15) ^ 0xffffcd7d;
h ^= (h >>> 10);
h += (h << 3);
h ^= (h >>> 6);
h += (h << 2) + (h << 14);
return h ^ (h >>> 16);
}
/**
* 通过散列值计算出在哪个 Segment 中
*/
final Segment<K,V> segmentFor(int hash) {
return segments[(hash >>> segmentShift) & segmentMask];
}
/**
* ConcurrentHashMap 的方法,如put、get等等
* 方法没有展示完
*/
public V put(K key, V value) {
....
}
}

具体的方法

注:这里要写 ConcurrentHashMap 的线程安全是怎么实现的,需要对JMM(Java内存模型)、volatile 变量、happens-before 等等并发知识进行了解。

put 操作

首先通过 key 的哈希值找到其对应的 Segment,然后该 Segment 对于的 put 方法

1
2
3
4
5
6
7
8
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
// 通过散列算法计算 key 的散列值
int hash = hash(key.hashCode());
// 根据散列码找到对应的 Segment
return segmentFor(hash).put(key, hash, value, false);
}

Segment 中的 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
V put(K key, int hash, V value, boolean onlyIfAbsent) {
// 获取锁,这里只是把该 Segment 锁住了,体现了分段锁的思想
lock();
try {
// count代表该 Segment 中HashEntry数目,其是volatile变量
// 注意:这里不能直接 count++,因为单个volatile变量的写才有原子性
// 像 volatile++ 这种复合操作是没有原子性的
int c = count;
// 判断该 Segment 能否存放
if (c++ > threshold) // ensure capacity
// 扩容
rehash();
HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
HashEntry<K,V> first = tab[index];
HashEntry<K,V> e = first;
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
V oldValue;
// 如果该 key 已经存在,则替换原 value,返回原 value
if (e != null) {
oldValue = e.value;
if (!onlyIfAbsent)
e.value = value;
}
// 该 key 没有存在,则向 HashMap 链表添加一个 HashMap
// 注意:只能在链表的头部添加,因为 HashEntry.next 是 final 类型。
else {
oldValue = null;
// 要添加新节点到链表中,链表的结构改变,所以 modCont 要加 1
++modCount;
tab[index] = new HashEntry<K,V>(key, hash, first, value);
// 改变 count 值,count 是 volatile 变量
// 注意:这里写 volatile 变量,会把变化的值向主内存中写,
// 并通知读 volatile 变量的线程从主内存中读取
// 这里与 get() 方法中对 count 的读取形成 happens-before 关系
count = c; // write-volatile
}
return oldValue;
} finally {
// 释放锁
unlock();
}
}

注意:这里仅仅对该 Segment 加锁了,没有对整个 ConcurrentHashMap 进行加锁。这里表现出分段锁思想,其它线程依然可以获取其他 Segment 的锁进行写操作。

扩容:这里是对单独的 Segment 的容量进行扩容,没有对这个容器进行扩容。

以上我们可以总结出:

  • Segment 数组的长度代表并发级别(理想状态下,数组的长度为 ConcurrentHasMap 可以支持线程并发操作的数目)
  • ConcurrentHashMap 的 size 是每个 Segment 的 count 之和

remove 操作

首先通过 key 的哈希值找到其对应的 Segment,然后该 Segment 对于的 remove 方法

1
2
3
4
public V remove(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).remove(key, hash, null);
}

Segment 中的 remocve 方法

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
V remove(Object key, int hash, Object value) {
// 获取锁,理由同上
lock();
try {
// 读取 count 值,理由同上
int c = count - 1;
HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
HashEntry<K,V> first = tab[index];
HashEntry<K,V> e = first;
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
V oldValue = null;
if (e != null) {
V v = e.value;
// 找到要删除的节点
if (value == null || value.equals(v)) {
oldValue = v;
// 要删除一个节点,链表的结构改变,所以 modCont 要加 1
++modCount;
// 这里删除一个节点,写法特别,下面会分析
HashEntry<K,V> newFirst = e.next;
for (HashEntry<K,V> p = first; p != e; p = p.next)
newFirst = new HashEntry<K,V>(p.key, p.hash, newFirst, p.value);
tab[index] = newFirst;
// 写 count 的值,理由同上
count = c; // write-volatile
}
}
return oldValue;
} finally {
unlock();
}
}

注意:这里的删除并不是直接的删除该节点,因为 HashEntry.next 是 final 类型,不能直接删除。它首先找到要删除的那个节点,然后把删除那个节点之前的没有节点中的值复制到新节点中,最后构成一个新链条。具体如下:

执行删除之前的原链表:

执行删除之后的新链表:

从上图可以看出,删除节点 C 之后的所有节点原样保留到新链表中;删除节点 C 之前的每个节点被克隆到新链表中,注意:它们在新链表中的链接顺序被反转了。

在执行 remove 操作时,原始链表的结构并没有被修改,综合上面的分析我们可以看出,写线程对某个链表的结构性修改不会影响其他的并发读线程对这个链表的遍历访问。

get操作

首先通过 key 的哈希值找到其对应的 Segment,然后该 Segment 对于的 get 方法

1
2
3
4
public V get(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}

Segment 中的 get方法

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
V get(Object key, int hash) {
// 读 count 值,count 是 volatile 变量
// 注意:这里是读 volatile 变量,它会从主内存中读取
// 它始终会读取到 volatile 更新后的内存中变化
if (count != 0) { // read-volatile
HashEntry<K,V> e = getFirst(hash);
while (e != null) {
if (e.hash == hash && key.equals(e.key)) {
V v = e.value;
if (v != null)
return v;
// 如果为 null,加锁后读
return readValueUnderLock(e); // recheck
}
e = e.next;
}
return null;
}

/**
* 加锁读的方法
*/
V readValueUnderLock(HashEntry<K,V> e) {
lock();
try {
return e.value;
} finally {
unlock();
}
}

我们可以看到其他的写操作(如 put、remove等)都加锁了的,所以体现出并发性。而这里读(get、contains等)没有进行加锁,这里使用了 volatile 类型的内存可见性:**写线程修改了 volatile 变量,读线程一定能够读到修改后的值。**所以这里可以回答 HashEntry 中 value 为什么是 volatile 变量,以及 count 为什么是 volatile 变量:因为每次写线程修改后的值,读线程一定能够读到。至于为什么 HashEntry.next 为何是 final 类型的:因为在修改链表结构时(remove、put等),写线程不会对原链表结构进行修改,所以读线程在不加锁的情况下仍然可以对该链表进行遍历访问。

size()操作

统计整个 ConcurrentHashMap 的 size 大小,就是必须统计每个 Segment 的 count 大小后求和。其中 Segment 中的 count 变量是 volatile 变量,每个 count 变量都是最。但是如果我们累加后,就不一定会得到 ConcurrentHashMap 的大小,因为如果在累加时使用过的 count 发生改变,那么结果就不准确了。如果统计大小时,锁住会导致效率低下。因为在累加 count 的操作过程中,之前累加 count 的值发生变化的几率不大。所以,在 JDK 1.6 中通过连续统计两次,比较两次统计结果,如果发生变化则会加锁方式。

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
public int size() {
final Segment<K,V>[] segments = this.segments;
long sum = 0;
long check = 0;
int[] mc = new int[segments.length];
// Try a few times to get accurate count. On failure due to
// continuous async changes in table, resort to locking.
for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) {
check = 0;
sum = 0;
int mcsum = 0;
for (int i = 0; i < segments.length; ++i) {
sum += segments[i].count;
mcsum += mc[i] = segments[i].modCount;
}
if (mcsum != 0) {
for (int i = 0; i < segments.length; ++i) {
check += segments[i].count;
if (mc[i] != segments[i].modCount) {
check = -1; // force retry
break;
}
}
}
if (check == sum)
break;
}
if (check != sum) { // Resort to locking all segments
sum = 0;
for (int i = 0; i < segments.length; ++i)
segments[i].lock();
for (int i = 0; i < segments.length; ++i)
sum += segments[i].count;
for (int i = 0; i < segments.length; ++i)
segments[i].unlock();
}
if (sum > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
else
return (int)sum;
}

总结

  • ConcurrentHashMap 不同于使用同步包装器包装的 HashMap(Collections.synchronizedMap(new HashMap()) 或者 HashTable 等使用全局锁来进行并发访问的控制,其使用了分段锁机制实现了多个线程同时写不会等待。
  • ConcurrentHashMap 利用了 volatile 变量的内存可见性,以及 HashEntery 链表的不变性让读操作可以不进行加锁。

应用

自己模拟JDK1.6中ConcurrentHashMap中的实现写了一下,有些方法还是不明白,就直接复制的JDK中的源码,如entrySet()方法的实现。

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
class ConcurrentHashMap<K, V> extends AbstractMap<K, V> {

// ------------------------------Construct method
@SuppressWarnings("unchecked")
public ConcurrentHashMap() {
this.segments = new Segment[DEFAULT_CONCURRENCY_LEVEL];
for (int i = 0; i < segments.length; i++) {
segments[i] = new Segment<>(DEFAULT_INITIAL_CAPACITY);
}
}

// -----------------------------Field
/**
* 散列映射表的默认初始容量为 16,即初始默认为16个桶
* 在构造函数中没有指定这个参数时,使用本参数
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;

/**
* 散列表的默认并发级别为 16。该值表示当前更新线程的估计数
* 在构造函数中没有指定这个参数时,使用本参数
*/
static final int DEFAULT_CONCURRENCY_LEVEL = 16;

static final int SEGMENT_SHIFT = 28;

static final int SEGMENT_MASK = 15;



/**
* 由Segment对象组成的数组
*/
final Segment<K, V>[] segments;

transient Set<Entry<K,V>> entrySet;

// --------------------------Some util methods
/**
* 哈希再散列,减少哈希冲突:JDK1.6算法
* @param h key的哈希值
* @return 再散列后的哈希值
*/
private int hash(int h) {
h += (h << 15) ^ 0xffffcd7d;
h ^= (h >>> 10);
h += (h << 3);
h ^= (h >>> 6);
h += (h << 2) + (h << 14);
return h ^ (h >>> 16);
}

/**
* 通过key散列后的哈希值来得到segments数组中对应的 Segment
* @param hash key再散列后的哈希值
* @return Segment segment
*/
private Segment<K,V> segmentFor(int hash) {
return segments[(hash >>> SEGMENT_SHIFT) & SEGMENT_MASK];
}

// -------------------------------Some public methods
@Override
public V get(Object key) {
if (key == null) {
throw new NullPointerException();
}
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}

@Override
public V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException();
}
int hash = hash(key.hashCode());
return segmentFor(hash).put(key, value, hash);
}

@Override
public boolean remove(Object key, Object value) {
if (key == null || value == null) {
throw new NullPointerException();
}
int hash = hash(key.hashCode());
return segmentFor(hash).remove(key, value, hash) != null;
}

@Override
public V remove(Object key) {
if (key == null) {
throw new NullPointerException();
}
int hash = hash(key.hashCode());
V value = segmentFor(hash).get(key, hash);
return segmentFor(hash).remove(key, value, hash);
}

@Override
public int size() {
final Segment<K,V>[] segments = this.segments;
long sum = 0;
long check = 0;
int[] mc = new int[segments.length];
// Try a few times to get accurate count. On failure due to
// continuous async changes in table, resort to locking.
for (int k = 0; k < 2; ++k) {
check = 0;
sum = 0;
int mcsum = 0;
for (int i = 0; i < segments.length; ++i) {
sum += segments[i].count;
mcsum += mc[i] = segments[i].modCount;
}
if (mcsum != 0) {
for (int i = 0; i < segments.length; ++i) {
check += segments[i].count;
if (mc[i] != segments[i].modCount) {
check = -1; // force retry
break;
}
}
}
if (check == sum)
break;
}
if (check != sum) { // Resort to locking all segments
sum = 0;
for (int i = 0; i < segments.length; ++i)
segments[i].lock();
for (int i = 0; i < segments.length; ++i)
sum += segments[i].count;
for (int i = 0; i < segments.length; ++i)
segments[i].unlock();
}
if (sum > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
else
return (int)sum;
}

@Override
public void clear() {
for (int i = 0; i < segments.length; i++)
segments[i].clear();
}

@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K,V>> es = entrySet;
return (es != null) ? es : (entrySet = new EntrySet());
}


// ----------------------------Inner class
static class Segment<K, V> extends ReentrantLock implements Serializable {
// --------------------Construct method
@SuppressWarnings("unchecked")
Segment(int initialCapacity) {
table = new Node[initialCapacity];
}
// ---------------------Field
private static final long serialVersionUID = 7249069246763182397L;
// 该Segment中Node的数目
volatile int count;
// 该Segment中Node[]结构的变化次数
int modCount;
// 存放Node的数组
volatile Node<K, V>[] table;


// ----------------------Some methods
/**
* 根据key再散列后的哈希值,返回segment中第一个链表节点
* @param hash key再散列后的哈希值
* @return 返回segment中第一个链表节点
*/
Node<K, V> getFirst(int hash) {
return table[hash & (table.length - 1)];
}

V get(Object key, int hash) {
// 读volatile变量,获取主内存中共享变量
if (count != 0) {
Node<K, V> node = getFirst(hash);
while (node != null) {
if (key.equals(node.key) && node.hash == hash) {
V value = node.getValue();
if (value != null) {
return value;
}
// 如果value为null,说明发生了重排序,加锁后重读
return readValueUnderLock(node); // recheck
}
node = node.next;
}
return null;
}
return null;
}

V readValueUnderLock(Node<K,V> e) {
lock(); // 获取锁
try {
return e.value;
} finally {
unlock(); // 释放锁
}
}

V put(K key, V value, int hash) {
lock(); // 获取锁
try {
// 这里c是用来验证是否超过容量的,我没有写扩容机制。
// 虽然看起来这里如果没有扩容机制的话,就可以不使用本地变量c,
// 但是这里必须使用本地变量把count的值加一,不能直接count++,
// 因为只有单个volatile变量的写才有原子性,如果是volatile++,则不具有原子性。
// 而且我们要先读volatile变量才能获取主内存中的共享变量。
int c = count;
c++;
Node<K, V> node = getFirst(hash);
Node<K, V> e = null;
while (node != null) {
if (key.equals(node.key) && value.equals(node.value) && hash == node.hash) {
e = node;
}
node = node.next;
}
// 如果该key、value存在,则修改后返回原值,且结果没有变化。
if (e != null) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
// 该key、value不存在,则添加一个新节点添加到链表头:
// 这样的话原链表结构不会发生变化,使得其他读线程正常遍历这个链表。
Node<K, V> first = getFirst(hash);
e = new Node<>(hash, first, key, value);
table[hash & (table.length - 1)] = e;
// 因为添加新节点了,所以modCount要加1
modCount++;
// count数量加1:写volatile变量使得该修改对所有读都有效。
count = c;
return value;
} finally {
unlock(); // 释放锁
}
}

V remove(Object key, Object value, int hash) {
lock(); // 获取锁
try {
// 与put方法中意义相似,就解释了
int c = count;
c--;
Node<K, V> node = getFirst(hash);
Node<K, V> e = null;
while (node != null) {
if (node.hash == hash && node.key.equals(key) && node.value.equals(value)) {
e = node;
}
node = node.next;
}
// 该key、value不存在,返回null
if (e == null) {
return null;
}
// 该key、value存在,需要删除一个节点
// 这里的删除没有真正意义上的删除,它新建了一个链表
// 使得原链表结果没有发生变化,其他读线程正常遍历这个链表
V oldValue = e.value;
Node<K, V> newFirst = e.next;
Node<K, V> first = getFirst(hash);
while (first != e) {
newFirst = new Node<>(first.hash, newFirst, first.key, first.value);
first = first.next;
}
table[hash & (table.length - 1)] = newFirst;
// 因为删除了一个节点,所以modCount要加1
modCount++;
// 写volatile变量使得该修改对所有读都有效。
count = c;
return oldValue;
} finally {
unlock(); // 释放锁
}
}

void clear() {
if (count != 0) {
lock(); // 获取锁
try {
Node<K,V>[] tab = table;
for (int i = 0; i < tab.length ; i++)
tab[i] = null;
// 因为删除了一个节点,所以modCount要加1
++modCount;
// 写volatile变量使得该修改对所有读都有效。
count = 0;
} finally {
unlock(); // 释放锁
}
}
}
}

static class Node<K, V> implements Entry<K, V> {
final int hash;
final Node<K, V> next;
final K key;
volatile V value;

public Node(int hash, Node<K, V> next, K key, V value) {
this.hash = hash;
this.next = next;
this.key = key;
this.value = value;
}

@Override
public K getKey() {
return key;
}

@Override
public V getValue() {
return value;
}

@Override
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
}



final class EntrySet extends AbstractSet<Entry<K,V>> {
public Iterator<Entry<K,V>> iterator() {
return new EntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Entry<?,?> e = (Entry<?,?>)o;
V v = ConcurrentHashMap.this.get(e.getKey());
return v != null && v.equals(e.getValue());
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Entry<?,?> e = (Entry<?,?>)o;
return ConcurrentHashMap.this.remove(e.getKey(), e.getValue());
}
public int size() {
return ConcurrentHashMap.this.size();
}
public void clear() {
ConcurrentHashMap.this.clear();
}
}

final class EntryIterator extends HashIterator implements Iterator<Entry<K,V>> {
public Entry<K,V> next() {
Node<K,V> e = super.nextEntry();
return new WriteThroughEntry(e.key, e.value);
}
}

final class WriteThroughEntry
extends SimpleEntry<K,V>
{
WriteThroughEntry(K k, V v) {
super(k,v);
}

/**
* Set our entry's value and write through to the map. The
* value to return is somewhat arbitrary here. Since a
* WriteThroughEntry does not necessarily track asynchronous
* changes, the most recent "previous" value could be
* different from what we return (or could even have been
* removed in which case the put will re-establish). We do not
* and cannot guarantee more.
*/
public V setValue(V value) {
if (value == null) throw new NullPointerException();
V v = super.setValue(value);
ConcurrentHashMap.this.put(getKey(), value);
return v;
}
}

/* ---------------- Iterator Support -------------- */

abstract class HashIterator {
int nextSegmentIndex;
int nextTableIndex;
Node<K,V>[] currentTable;
Node<K, V> nextEntry;
Node<K, V> lastReturned;

HashIterator() {
nextSegmentIndex = segments.length - 1;
nextTableIndex = -1;
advance();
}

public boolean hasMoreElements() { return hasNext(); }

final void advance() {
if (nextEntry != null && (nextEntry = nextEntry.next) != null)
return;

while (nextTableIndex >= 0) {
if ( (nextEntry = currentTable[nextTableIndex--]) != null)
return;
}

while (nextSegmentIndex >= 0) {
Segment<K,V> seg = segments[nextSegmentIndex--];
if (seg.count != 0) {
currentTable = seg.table;
for (int j = currentTable.length - 1; j >= 0; --j) {
if ( (nextEntry = currentTable[j]) != null) {
nextTableIndex = j - 1;
return;
}
}
}
}
}

public boolean hasNext() { return nextEntry != null; }

Node<K,V> nextEntry() {
if (nextEntry == null)
throw new NoSuchElementException();
lastReturned = nextEntry;
advance();
return lastReturned;
}

public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
ConcurrentHashMap.this.remove(lastReturned.key);
lastReturned = null;
}
}
}

References

探索 ConcurrentHashMap 高并发性的实现机制
《Java并发编程的艺术》