ConcurrentHashMap详解
ConcurrentHashMap详解
注:该文章主要讲的是JDK1.6中ConcurrentHashMap的实现,JDK1.8中ConcurrentHashMap的实现由不同的机制,详解可看:ConcurrentHashMap总结
1 概述
public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable {
- ConcurrentHashMap内部使用了分段锁+哈希数组+链表的实现(JDK1.6)
- ConcurrentHashMap是遍历无序、线程安全
- ConcurrentHashMap的迭代器不是快速失败的,也就是说我们在遍历的时候可以对ConcurrentHashMap的结构进行修改,而不会抛出 ConcurrentModificationException异常
- HashMap中key不能有重复元素,并且key与value都不能为null
2 实现机制
2.1 锁分段技术(分拆锁技术)
《Java并发编程艺术》
HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一>部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞>>争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段>技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
《Java并发编程实践》
分拆锁(lock spliting)就是若原先的程序中多处逻辑都采用同一个锁,但各个逻辑之间又相互独立,就可以拆(Spliting)为使用多个锁,每个锁守护不同的逻辑。 分拆锁有时候可以被扩展,分成可大可小加锁块的集合,并且它们归属于相互独立的对象,这样的情况就是分离锁(lock striping)。
2.2 ConcurrentHashMap结构
ConcurrentHashMap的结构与HashMap的结构类似,只是在中间加了一层锁(Segment)。其类图如下:
ConcurrentHashMap是有Segment数组组成,每个Segment由HashEntry数组组成,每个HashEntry数组中存放的是HashEntry链表。其中Segment是可重入锁ReentrantLock,它代表锁结构,每个Segment守护它自己包含的HashEntry数组中的元素,这样就体现出锁分段技术。我们在并发访问时,该Segment锁仅仅锁住它自己的一部分数据,从而不会影响其他部分的数据的读写。其结构图如下:
3 一些类的结构分析
注:自己在分析时,有许多的方法都没有分析,主要分析了几个方法的逻辑,其他方法类似。下面的类来自JDK1.6,自己删除了一些字段与方法。
3.1 HashEntry类
- HashEntry用来封装key、value
- key,hash 和 next 域都被声明为 final 型,至于为何为 final变量,后面会分析
- value 域被声明为 volatile 型,至于为何为 volatile 变量,后面会分析
注:next申明为final类型,说明HashEntry链表只能从头插入,而且不能删除。
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;
}
}
3.2 Segment类
- Segment 类继承 ReentrantLock,用来守护其含有的数据
- count 代表该 Segment 中 HashEnty 的数量,是 volatile 类型。
- modCount 代表该 Segment 结构改变的次数
- loadFactor 代表负载因子;threshold 代表该 Segment 最大容量(这两个字段与HashMap中意义相似,用来扩容的)
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) {
...
}
}
3.3 ConcurrentHashMap 类
注:还有许多的方法没有列出来
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) {
....
}
}
4 具体的操作
注:这里要写 ConcurrentHashMap 的线程安全是怎么实现的,需要对JMM(Java内存模型)、volatile 变量、happens-before 等等并发知识进行了解,具体可见:Java并发编程(一)
4.1 put 操作
首先通过 key 的哈希值找到其对应的 Segment,然后该 Segment 对于的 put 方法
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 方法
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 之和
4.2 remove 操作
首先通过 key 的哈希值找到其对应的 Segment,然后该 Segment 对于的 remove 方法
public V remove(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).remove(key, hash, null);
}
Segment 中的 remocve 方法
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 操作时,原始链表的结构并没有被修改,综合上面的分析我们可以看出,写线程对某个链表的结构性修改不会影响其他的并发读线程对这个链表的遍历访问。
4.3 get 操作
首先通过 key 的哈希值找到其对应的 Segment,然后该 Segment 对于的 remove 方法
public V get(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}
Segment 中的 get方法
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等),写线程不会对原链表结构进行修改,所以读线程在不加锁的情况下仍然可以对该链表进行遍历访问。
4.3 size 操作
统计整个 ConcurrentHashMap 的 size 大小,就是必须统计每个 Segment 的 count 大小后求和。其中 Segment 中的 count 变量是 volatile 变量,每个 count 变量都是最。但是如果我们累加后,就不一定会得到 ConcurrentHashMap 的大小,因为如果在累加时使用过的 count 发生改变,那么结果就不准确了。如果统计大小时,锁住会导致效率低下。因为在累加 count 的操作过程中,之前累加 count 的值发生变化的几率不大。所以,在 JDK 1.6 中通过连续统计两次,比较两次统计结果,如果发生变化则会加锁方式。
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;
}
5 总结
- ConcurrentHashMap 不同于使用同步包装器包装的 HashMap(Collections.synchronizedMap(new HashMap()) 或者 HashTable 等使用全局锁来进行并发访问的控制,其使用了分段锁机制实现了多个线程同时写不会等待。
- ConcurrentHashMap 利用了 volatile 变量的内存可见性,以及 HashEntery 链表的不变性让读操作可以不进行加锁。
6 应用
自己模拟JDK1.6中ConcurrentHashMap中的实现写了一下,有些方法还是不明白,就直接复制的JDK中的源码,如entrySet()方法的实现。
1 class ConcurrentHashMap<K, V> extends AbstractMap<K, V> { 2 3 // ------------------------------Construct method 4 @SuppressWarnings("unchecked") 5 public ConcurrentHashMap() { 6 this.segments = new Segment[DEFAULT_CONCURRENCY_LEVEL]; 7 for (int i = 0; i < segments.length; i++) { 8 segments[i] = new Segment<>(DEFAULT_INITIAL_CAPACITY); 9 } 10 } 11 12 // -----------------------------Field 13 /** 14 * 散列映射表的默认初始容量为 16,即初始默认为16个桶 15 * 在构造函数中没有指定这个参数时,使用本参数 16 */ 17 static final int DEFAULT_INITIAL_CAPACITY = 16; 18 19 /** 20 * 散列表的默认并发级别为 16。该值表示当前更新线程的估计数 21 * 在构造函数中没有指定这个参数时,使用本参数 22 */ 23 static final int DEFAULT_CONCURRENCY_LEVEL = 16; 24 25 static final int SEGMENT_SHIFT = 28; 26 27 static final int SEGMENT_MASK = 15; 28 29 30 31 /** 32 * 由Segment对象组成的数组 33 */ 34 final Segment<K, V>[] segments; 35 36 transient Set<Entry<K,V>> entrySet; 37 38 // --------------------------Some util methods 39 /** 40 * 哈希再散列,减少哈希冲突:JDK1.6算法 41 * @param h key的哈希值 42 * @return 再散列后的哈希值 43 */ 44 private int hash(int h) { 45 h += (h << 15) ^ 0xffffcd7d; 46 h ^= (h >>> 10); 47 h += (h << 3); 48 h ^= (h >>> 6); 49 h += (h << 2) + (h << 14); 50 return h ^ (h >>> 16); 51 } 52 53 /** 54 * 通过key散列后的哈希值来得到segments数组中对应的 Segment 55 * @param hash key再散列后的哈希值 56 * @return Segment segment 57 */ 58 private Segment<K,V> segmentFor(int hash) { 59 return segments[(hash >>> SEGMENT_SHIFT) & SEGMENT_MASK]; 60 } 61 62 // -------------------------------Some public methods 63 @Override 64 public V get(Object key) { 65 if (key == null) { 66 throw new NullPointerException(); 67 } 68 int hash = hash(key.hashCode()); 69 return segmentFor(hash).get(key, hash); 70 } 71 72 @Override 73 public V put(K key, V value) { 74 if (key == null || value == null) { 75 throw new NullPointerException(); 76 } 77 int hash = hash(key.hashCode()); 78 return segmentFor(hash).put(key, value, hash); 79 } 80 81 @Override 82 public boolean remove(Object key, Object value) { 83 if (key == null || value == null) { 84 throw new NullPointerException(); 85 } 86 int hash = hash(key.hashCode()); 87 return segmentFor(hash).remove(key, value, hash) != null; 88 } 89 90 @Override 91 public V remove(Object key) { 92 if (key == null) { 93 throw new NullPointerException(); 94 } 95 int hash = hash(key.hashCode()); 96 V value = segmentFor(hash).get(key, hash); 97 return segmentFor(hash).remove(key, value, hash); 98 } 99 100 @Override 101 public int size() { 102 final Segment<K,V>[] segments = this.segments; 103 long sum = 0; 104 long check = 0; 105 int[] mc = new int[segments.length]; 106 // Try a few times to get accurate count. On failure due to 107 // continuous async changes in table, resort to locking. 108 for (int k = 0; k < 2; ++k) { 109 check = 0; 110 sum = 0; 111 int mcsum = 0; 112 for (int i = 0; i < segments.length; ++i) { 113 sum += segments[i].count; 114 mcsum += mc[i] = segments[i].modCount; 115 } 116 if (mcsum != 0) { 117 for (int i = 0; i < segments.length; ++i) { 118 check += segments[i].count; 119 if (mc[i] != segments[i].modCount) { 120 check = -1; // force retry 121 break; 122 } 123 } 124 } 125 if (check == sum) 126 break; 127 } 128 if (check != sum) { // Resort to locking all segments 129 sum = 0; 130 for (int i = 0; i < segments.length; ++i) 131 segments[i].lock(); 132 for (int i = 0; i < segments.length; ++i) 133 sum += segments[i].count; 134 for (int i = 0; i < segments.length; ++i) 135 segments[i].unlock(); 136 } 137 if (sum > Integer.MAX_VALUE) 138 return Integer.MAX_VALUE; 139 else 140 return (int)sum; 141 } 142 143 @Override 144 public void clear() { 145 for (int i = 0; i < segments.length; i++) 146 segments[i].clear(); 147 } 148 149 @Override 150 public Set<Entry<K, V>> entrySet() { 151 Set<Entry<K,V>> es = entrySet; 152 return (es != null) ? es : (entrySet = new EntrySet()); 153 } 154 155 156 // ----------------------------Inner class 157 static class Segment<K, V> extends ReentrantLock implements Serializable { 158 // --------------------Construct method 159 @SuppressWarnings("unchecked") 160 Segment(int initialCapacity) { 161 table = new Node[initialCapacity]; 162 } 163 // ---------------------Field 164 private static final long serialVersionUID = 7249069246763182397L; 165 // 该Segment中Node的数目 166 volatile int count; 167 // 该Segment中Node[]结构的变化次数 168 int modCount; 169 // 存放Node的数组 170 volatile Node<K, V>[] table; 171 172 173 // ----------------------Some methods 174 /** 175 * 根据key再散列后的哈希值,返回segment中第一个链表节点 176 * @param hash key再散列后的哈希值 177 * @return 返回segment中第一个链表节点 178 */ 179 Node<K, V> getFirst(int hash) { 180 return table[hash & (table.length - 1)]; 181 } 182 183 V get(Object key, int hash) { 184 // 读volatile变量,获取主内存中共享变量 185 if (count != 0) { 186 Node<K, V> node = getFirst(hash); 187 while (node != null) { 188 if (key.equals(node.key) && node.hash == hash) { 189 V value = node.getValue(); 190 if (value != null) { 191 return value; 192 } 193 // 如果value为null,说明发生了重排序,加锁后重读 194 return readValueUnderLock(node); // recheck 195 } 196 node = node.next; 197 } 198 return null; 199 } 200 return null; 201 } 202 203 V readValueUnderLock(Node<K,V> e) { 204 lock(); // 获取锁 205 try { 206 return e.value; 207 } finally { 208 unlock(); // 释放锁 209 } 210 } 211 212 V put(K key, V value, int hash) { 213 lock(); // 获取锁 214 try { 215 // 这里c是用来验证是否超过容量的,我没有写扩容机制。 216 // 虽然看起来这里如果没有扩容机制的话,就可以不使用本地变量c, 217 // 但是这里必须使用本地变量把count的值加一,不能直接count++, 218 // 因为只有单个volatile变量的写才有原子性,如果是volatile++,则不具有原子性。 219 // 而且我们要先读volatile变量才能获取主内存中的共享变量。 220 int c = count; 221 c++; 222 Node<K, V> node = getFirst(hash); 223 Node<K, V> e = null; 224 while (node != null) { 225 if (key.equals(node.key) && value.equals(node.value) && hash == node.hash) { 226 e = node; 227 } 228 node = node.next; 229 } 230 // 如果该key、value存在,则修改后返回原值,且结果没有变化。 231 if (e != null) { 232 V oldValue = e.value; 233 e.value = value; 234 return oldValue; 235 } 236 // 该key、value不存在,则添加一个新节点添加到链表头: 237 // 这样的话原链表结构不会发生变化,使得其他读线程正常遍历这个链表。 238 Node<K, V> first = getFirst(hash); 239 e = new Node<>(hash, first, key, value); 240 table[hash & (table.length - 1)] = e; 241 // 因为添加新节点了,所以modCount要加1 242 modCount++; 243 // count数量加1:写volatile变量使得该修改对所有读都有效。 244 count = c; 245 return value; 246 } finally { 247 unlock(); // 释放锁 248 } 249 } 250 251 V remove(Object key, Object value, int hash) { 252 lock(); // 获取锁 253 try { 254 // 与put方法中意义相似,就解释了 255 int c = count; 256 c--; 257 Node<K, V> node = getFirst(hash); 258 Node<K, V> e = null; 259 while (node != null) { 260 if (node.hash == hash && node.key.equals(key) && node.value.equals(value)) { 261 e = node; 262 } 263 node = node.next; 264 } 265 // 该key、value不存在,返回null 266 if (e == null) { 267 return null; 268 } 269 // 该key、value存在,需要删除一个节点 270 // 这里的删除没有真正意义上的删除,它新建了一个链表 271 // 使得原链表结果没有发生变化,其他读线程正常遍历这个链表 272 V oldValue = e.value; 273 Node<K, V> newFirst = e.next; 274 Node<K, V> first = getFirst(hash); 275 while (first != e) { 276 newFirst = new Node<>(first.hash, newFirst, first.key, first.value); 277 first = first.next; 278 } 279 table[hash & (table.length - 1)] = newFirst; 280 // 因为删除了一个节点,所以modCount要加1 281 modCount++; 282 // 写volatile变量使得该修改对所有读都有效。 283 count = c; 284 return oldValue; 285 } finally { 286 unlock(); // 释放锁 287 } 288 } 289 290 void clear() { 291 if (count != 0) { 292 lock(); // 获取锁 293 try { 294 Node<K,V>[] tab = table; 295 for (int i = 0; i < tab.length ; i++) 296 tab[i] = null; 297 // 因为删除了一个节点,所以modCount要加1 298 ++modCount; 299 // 写volatile变量使得该修改对所有读都有效。 300 count = 0; 301 } finally { 302 unlock(); // 释放锁 303 } 304 } 305 } 306 } 307 308 static class Node<K, V> implements Entry<K, V> { 309 final int hash; 310 final Node<K, V> next; 311 final K key; 312 volatile V value; 313 314 public Node(int hash, Node<K, V> next, K key, V value) { 315 this.hash = hash; 316 this.next = next; 317 this.key = key; 318 this.value = value; 319 } 320 321 @Override 322 public K getKey() { 323 return key; 324 } 325 326 @Override 327 public V getValue() { 328 return value; 329 } 330 331 @Override 332 public V setValue(V value) { 333 V oldValue = this.value; 334 this.value = value; 335 return oldValue; 336 } 337 } 338 339 340 341 final class EntrySet extends AbstractSet<Entry<K,V>> { 342 public Iterator<Entry<K,V>> iterator() { 343 return new EntryIterator(); 344 } 345 public boolean contains(Object o) { 346 if (!(o instanceof Map.Entry)) 347 return false; 348 Entry<?,?> e = (Entry<?,?>)o; 349 V v = ConcurrentHashMap.this.get(e.getKey()); 350 return v != null && v.equals(e.getValue()); 351 } 352 public boolean remove(Object o) { 353 if (!(o instanceof Map.Entry)) 354 return false; 355 Entry<?,?> e = (Entry<?,?>)o; 356 return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()); 357 } 358 public int size() { 359 return ConcurrentHashMap.this.size(); 360 } 361 public void clear() { 362 ConcurrentHashMap.this.clear(); 363 } 364 } 365 366 final class EntryIterator extends HashIterator implements Iterator<Entry<K,V>> { 367 public Entry<K,V> next() { 368 Node<K,V> e = super.nextEntry(); 369 return new WriteThroughEntry(e.key, e.value); 370 } 371 } 372 373 final class WriteThroughEntry 374 extends SimpleEntry<K,V> 375 { 376 WriteThroughEntry(K k, V v) { 377 super(k,v); 378 } 379 380 /** 381 * Set our entry's value and write through to the map. The 382 * value to return is somewhat arbitrary here. Since a 383 * WriteThroughEntry does not necessarily track asynchronous 384 * changes, the most recent "previous" value could be 385 * different from what we return (or could even have been 386 * removed in which case the put will re-establish). We do not 387 * and cannot guarantee more. 388 */ 389 public V setValue(V value) { 390 if (value == null) throw new NullPointerException(); 391 V v = super.setValue(value); 392 ConcurrentHashMap.this.put(getKey(), value); 393 return v; 394 } 395 } 396 397 /* ---------------- Iterator Support -------------- */ 398 399 abstract class HashIterator { 400 int nextSegmentIndex; 401 int nextTableIndex; 402 Node<K,V>[] currentTable; 403 Node<K, V> nextEntry; 404 Node<K, V> lastReturned; 405 406 HashIterator() { 407 nextSegmentIndex = segments.length - 1; 408 nextTableIndex = -1; 409 advance(); 410 } 411 412 public boolean hasMoreElements() { return hasNext(); } 413 414 final void advance() { 415 if (nextEntry != null && (nextEntry = nextEntry.next) != null) 416 return; 417 418 while (nextTableIndex >= 0) { 419 if ( (nextEntry = currentTable[nextTableIndex--]) != null) 420 return; 421 } 422 423 while (nextSegmentIndex >= 0) { 424 Segment<K,V> seg = segments[nextSegmentIndex--]; 425 if (seg.count != 0) { 426 currentTable = seg.table; 427 for (int j = currentTable.length - 1; j >= 0; --j) { 428 if ( (nextEntry = currentTable[j]) != null) { 429 nextTableIndex = j - 1; 430 return; 431 } 432 } 433 } 434 } 435 } 436 437 public boolean hasNext() { return nextEntry != null; } 438 439 Node<K,V> nextEntry() { 440 if (nextEntry == null) 441 throw new NoSuchElementException(); 442 lastReturned = nextEntry; 443 advance(); 444 return lastReturned; 445 } 446 447 public void remove() { 448 if (lastReturned == null) 449 throw new IllegalStateException(); 450 ConcurrentHashMap.this.remove(lastReturned.key); 451 lastReturned = null; 452 } 453 } 454 }
7 Reference
探索 ConcurrentHashMap 高并发性的实现机制
《Java并发编程的艺术》