概要
本章是JUC系列的ConcurrentHashMap篇。内容包括:
ConcurrentHashMap介绍
ConcurrentHashMap原理和数据结构
ConcurrentHashMap函数列表
ConcurrentHashMap源码分析(JDK1.7.0_40版本)
ConcurrentHashMap示例
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3498537.html
ConcurrentHashMap介绍
ConcurrentHashMap是线程安全的哈希表。HashMap, Hashtable, ConcurrentHashMap之间的关联如下:
HashMap是非线程安全的哈希表,常用于单线程程序中。
Hashtable是线程安全的哈希表,它是通过synchronized来保证线程安全的;即,多线程通过同一个“对象的同步锁”来实现并发控制。Hashtable在线程竞争激烈时,效率比较低(此时建议使用ConcurrentHashMap)!因为当一个线程访问Hashtable的同步方法时,其它线程就访问Hashtable的同步方法时,可能会进入阻塞状态。
ConcurrentHashMap是线程安全的哈希表,它是通过“锁分段”来保证线程安全的。ConcurrentHashMap将哈希表分成许多片段(Segment),每一个片段除了保存哈希表之外,本质上也是一个“可重入的互斥锁”(ReentrantLock)。多线程对同一个片段的访问,是互斥的;但是,对于不同片段的访问,却是可以同步进行的。
关于HashMap,Hashtable以及ReentrantLock的更多内容,可以参考:
1. Java
集合系列10之 HashMap详细介绍(源码解析)和使用示例
2.
Java 集合系列11之
Hashtable详细介绍(源码解析)和使用示例
3. Java多线程系列--“JUC锁”02之
互斥锁ReentrantLock
ConcurrentHashMap原理和数据结构
要想搞清ConcurrentHashMap,必须先弄清楚它的数据结构:
(01)
ConcurrentHashMap继承于AbstractMap抽象类。
(02)
Segment是ConcurrentHashMap中的内部类,它就是ConcurrentHashMap中的“锁分段”对应的存储结构。ConcurrentHashMap与Segment是组合关系,1个ConcurrentHashMap对象包含若干个Segment对象。在代码中,这表现为ConcurrentHashMap类中存在“Segment数组”成员。
(03)
Segment类继承于ReentrantLock类,所以Segment本质上是一个可重入的互斥锁。
(04)
HashEntry也是ConcurrentHashMap的内部类,是单向链表节点,存储着key-value键值对。Segment与HashEntry是组合关系,Segment类中存在“HashEntry数组”成员,“HashEntry数组”中的每个HashEntry就是一个单向链表。
对于多线程访问对一个“哈希表对象”竞争资源,Hashtable是通过一把锁来控制并发;而ConcurrentHashMap则是将哈希表分成许多片段,对于每一个片段分别通过一个互斥锁来控制并发。ConcurrentHashMap对并发的控制更加细腻,它也更加适应于高并发场景!
ConcurrentHashMap函数列表
// 创建一个带有默认初始容量 (16)、加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射。 ConcurrentHashMap() // 创建一个带有指定初始容量、默认加载因子 (0.75) 和 concurrencyLevel (16) 的新的空映射。 ConcurrentHashMap(int initialCapacity) // 创建一个带有指定初始容量、加载因子和默认 concurrencyLevel (16) 的新的空映射。 ConcurrentHashMap(int initialCapacity, float loadFactor) // 创建一个带有指定初始容量、加载因子和并发级别的新的空映射。 ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) // 构造一个与给定映射具有相同映射关系的新映射。 ConcurrentHashMap(Map<? extends K,? extends V> m) // 从该映射中移除所有映射关系 void clear() // 一种遗留方法,测试此表中是否有一些与指定值存在映射关系的键。 boolean contains(Object value) // 测试指定对象是否为此表中的键。 boolean containsKey(Object key) // 如果此映射将一个或多个键映射到指定值,则返回 true。 boolean containsValue(Object value) // 返回此表中值的枚举。 Enumeration<V> elements() // 返回此映射所包含的映射关系的 Set 视图。 Set<Map.Entry<K,V>> entrySet() // 返回指定键所映射到的值,如果此映射不包含该键的映射关系,则返回 null。 V get(Object key) // 如果此映射不包含键-值映射关系,则返回 true。 boolean isEmpty() // 返回此表中键的枚举。 Enumeration<K> keys() // 返回此映射中包含的键的 Set 视图。 Set<K> keySet() // 将指定键映射到此表中的指定值。 V put(K key, V value) // 将指定映射中所有映射关系复制到此映射中。 void putAll(Map<? extends K,? extends V> m) // 如果指定键已经不再与某个值相关联,则将它与给定值关联。 V putIfAbsent(K key, V value) // 从此映射中移除键(及其相应的值)。 V remove(Object key) // 只有目前将键的条目映射到给定值时,才移除该键的条目。 boolean remove(Object key, Object value) // 只有目前将键的条目映射到某一值时,才替换该键的条目。 V replace(K key, V value) // 只有目前将键的条目映射到给定值时,才替换该键的条目。 boolean replace(K key, V oldValue, V newValue) // 返回此映射中的键-值映射关系数。 int size() // 返回此映射中包含的值的 Collection 视图。 Collection<V> values()
ConcurrentHashMap源码分析(JDK1.7.0_40版本)
ConcurrentHashMap.java的完整源码如下:
1 /* 2 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 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 * Written by Doug Lea with assistance from members of JCP JSR-166 32 * Expert Group and released to the public domain, as explained at 33 * http://creativecommons.org/publicdomain/zero/1.0/ 34 */ 35 36 package java.util.concurrent; 37 import java.util.concurrent.locks.*; 38 import java.util.*; 39 import java.io.Serializable; 40 import java.io.IOException; 41 import java.io.ObjectInputStream; 42 import java.io.ObjectOutputStream; 43 import java.io.ObjectStreamField; 44 45 /** 46 * A hash table supporting full concurrency of retrievals and 47 * adjustable expected concurrency for updates. This class obeys the 48 * same functional specification as {@link java.util.Hashtable}, and 49 * includes versions of methods corresponding to each method of 50 * <tt>Hashtable</tt>. However, even though all operations are 51 * thread-safe, retrieval operations do <em>not</em> entail locking, 52 * and there is <em>not</em> any support for locking the entire table 53 * in a way that prevents all access. This class is fully 54 * interoperable with <tt>Hashtable</tt> in programs that rely on its 55 * thread safety but not on its synchronization details. 56 * 57 * <p> Retrieval operations (including <tt>get</tt>) generally do not 58 * block, so may overlap with update operations (including 59 * <tt>put</tt> and <tt>remove</tt>). Retrievals reflect the results 60 * of the most recently <em>completed</em> update operations holding 61 * upon their onset. For aggregate operations such as <tt>putAll</tt> 62 * and <tt>clear</tt>, concurrent retrievals may reflect insertion or 63 * removal of only some entries. Similarly, Iterators and 64 * Enumerations return elements reflecting the state of the hash table 65 * at some point at or since the creation of the iterator/enumeration. 66 * They do <em>not</em> throw {@link ConcurrentModificationException}. 67 * However, iterators are designed to be used by only one thread at a time. 68 * 69 * <p> The allowed concurrency among update operations is guided by 70 * the optional <tt>concurrencyLevel</tt> constructor argument 71 * (default <tt>16</tt>), which is used as a hint for internal sizing. The 72 * table is internally partitioned to try to permit the indicated 73 * number of concurrent updates without contention. Because placement 74 * in hash tables is essentially random, the actual concurrency will 75 * vary. Ideally, you should choose a value to accommodate as many 76 * threads as will ever concurrently modify the table. Using a 77 * significantly higher value than you need can waste space and time, 78 * and a significantly lower value can lead to thread contention. But 79 * overestimates and underestimates within an order of magnitude do 80 * not usually have much noticeable impact. A value of one is 81 * appropriate when it is known that only one thread will modify and 82 * all others will only read. Also, resizing this or any other kind of 83 * hash table is a relatively slow operation, so, when possible, it is 84 * a good idea to provide estimates of expected table sizes in 85 * constructors. 86 * 87 * <p>This class and its views and iterators implement all of the 88 * <em>optional</em> methods of the {@link Map} and {@link Iterator} 89 * interfaces. 90 * 91 * <p> Like {@link Hashtable} but unlike {@link HashMap}, this class 92 * does <em>not</em> allow <tt>null</tt> to be used as a key or value. 93 * 94 * <p>This class is a member of the 95 * <a href="{@docRoot}/../technotes/guides/collections/index.html"> 96 * Java Collections Framework</a>. 97 * 98 * @since 1.5 99 * @author Doug Lea 100 * @param <K> the type of keys maintained by this map 101 * @param <V> the type of mapped values 102 */ 103 public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> 104 implements ConcurrentMap<K, V>, Serializable { 105 private static final long serialVersionUID = 7249069246763182397L; 106 107 /* 108 * The basic strategy is to subdivide the table among Segments, 109 * each of which itself is a concurrently readable hash table. To 110 * reduce footprint, all but one segments are constructed only 111 * when first needed (see ensureSegment). To maintain visibility 112 * in the presence of lazy construction, accesses to segments as 113 * well as elements of segment‘s table must use volatile access, 114 * which is done via Unsafe within methods segmentAt etc 115 * below. These provide the functionality of AtomicReferenceArrays 116 * but reduce the levels of indirection. Additionally, 117 * volatile-writes of table elements and entry "next" fields 118 * within locked operations use the cheaper "lazySet" forms of 119 * writes (via putOrderedObject) because these writes are always 120 * followed by lock releases that maintain sequential consistency 121 * of table updates. 122 * 123 * Historical note: The previous version of this class relied 124 * heavily on "final" fields, which avoided some volatile reads at 125 * the expense of a large initial footprint. Some remnants of 126 * that design (including forced construction of segment 0) exist 127 * to ensure serialization compatibility. 128 */ 129 130 /* ---------------- Constants -------------- */ 131 132 /** 133 * The default initial capacity for this table, 134 * used when not otherwise specified in a constructor. 135 */ 136 static final int DEFAULT_INITIAL_CAPACITY = 16; 137 138 /** 139 * The default load factor for this table, used when not 140 * otherwise specified in a constructor. 141 */ 142 static final float DEFAULT_LOAD_FACTOR = 0.75f; 143 144 /** 145 * The default concurrency level for this table, used when not 146 * otherwise specified in a constructor. 147 */ 148 static final int DEFAULT_CONCURRENCY_LEVEL = 16; 149 150 /** 151 * The maximum capacity, used if a higher value is implicitly 152 * specified by either of the constructors with arguments. MUST 153 * be a power of two <= 1<<30 to ensure that entries are indexable 154 * using ints. 155 */ 156 static final int MAXIMUM_CAPACITY = 1 << 30; 157 158 /** 159 * The minimum capacity for per-segment tables. Must be a power 160 * of two, at least two to avoid immediate resizing on next use 161 * after lazy construction. 162 */ 163 static final int MIN_SEGMENT_TABLE_CAPACITY = 2; 164 165 /** 166 * The maximum number of segments to allow; used to bound 167 * constructor arguments. Must be power of two less than 1 << 24. 168 */ 169 static final int MAX_SEGMENTS = 1 << 16; // slightly conservative 170 171 /** 172 * Number of unsynchronized retries in size and containsValue 173 * methods before resorting to locking. This is used to avoid 174 * unbounded retries if tables undergo continuous modification 175 * which would make it impossible to obtain an accurate result. 176 */ 177 static final int RETRIES_BEFORE_LOCK = 2; 178 179 /* ---------------- Fields -------------- */ 180 181 /** 182 * holds values which can‘t be initialized until after VM is booted. 183 */ 184 private static class Holder { 185 186 /** 187 * Enable alternative hashing of String keys? 188 * 189 * <p>Unlike the other hash map implementations we do not implement a 190 * threshold for regulating whether alternative hashing is used for 191 * String keys. Alternative hashing is either enabled for all instances 192 * or disabled for all instances. 193 */ 194 static final boolean ALTERNATIVE_HASHING; 195 196 static { 197 // Use the "threshold" system property even though our threshold 198 // behaviour is "ON" or "OFF". 199 String altThreshold = java.security.AccessController.doPrivileged( 200 new sun.security.action.GetPropertyAction( 201 "jdk.map.althashing.threshold")); 202 203 int threshold; 204 try { 205 threshold = (null != altThreshold) 206 ? Integer.parseInt(altThreshold) 207 : Integer.MAX_VALUE; 208 209 // disable alternative hashing if -1 210 if (threshold == -1) { 211 threshold = Integer.MAX_VALUE; 212 } 213 214 if (threshold < 0) { 215 throw new IllegalArgumentException("value must be positive integer."); 216 } 217 } catch(IllegalArgumentException failed) { 218 throw new Error("Illegal value for ‘jdk.map.althashing.threshold‘", failed); 219 } 220 ALTERNATIVE_HASHING = threshold <= MAXIMUM_CAPACITY; 221 } 222 } 223 224 /** 225 * A randomizing value associated with this instance that is applied to 226 * hash code of keys to make hash collisions harder to find. 227 */ 228 private transient final int hashSeed = randomHashSeed(this); 229 230 private static int randomHashSeed(ConcurrentHashMap instance) { 231 if (sun.misc.VM.isBooted() && Holder.ALTERNATIVE_HASHING) { 232 return sun.misc.Hashing.randomHashSeed(instance); 233 } 234 235 return 0; 236 } 237 238 /** 239 * Mask value for indexing into segments. The upper bits of a 240 * key‘s hash code are used to choose the segment. 241 */ 242 final int segmentMask; 243 244 /** 245 * Shift value for indexing within segments. 246 */ 247 final int segmentShift; 248 249 /** 250 * The segments, each of which is a specialized hash table. 251 */ 252 final Segment<K,V>[] segments; 253 254 transient Set<K> keySet; 255 transient Set<Map.Entry<K,V>> entrySet; 256 transient Collection<V> values; 257 258 /** 259 * ConcurrentHashMap list entry. Note that this is never exported 260 * out as a user-visible Map.Entry. 261 */ 262 static final class HashEntry<K,V> { 263 final int hash; 264 final K key; 265 volatile V value; 266 volatile HashEntry<K,V> next; 267 268 HashEntry(int hash, K key, V value, HashEntry<K,V> next) { 269 this.hash = hash; 270 this.key = key; 271 this.value = value; 272 this.next = next; 273 } 274 275 /** 276 * Sets next field with volatile write semantics. (See above 277 * about use of putOrderedObject.) 278 */ 279 final void setNext(HashEntry<K,V> n) { 280 UNSAFE.putOrderedObject(this, nextOffset, n); 281 } 282 283 // Unsafe mechanics 284 static final sun.misc.Unsafe UNSAFE; 285 static final long nextOffset; 286 static { 287 try { 288 UNSAFE = sun.misc.Unsafe.getUnsafe(); 289 Class k = HashEntry.class; 290 nextOffset = UNSAFE.objectFieldOffset 291 (k.getDeclaredField("next")); 292 } catch (Exception e) { 293 throw new Error(e); 294 } 295 } 296 } 297 298 /** 299 * Gets the ith element of given table (if nonnull) with volatile 300 * read semantics. Note: This is manually integrated into a few 301 * performance-sensitive methods to reduce call overhead. 302 */ 303 @SuppressWarnings("unchecked") 304 static final <K,V> HashEntry<K,V> entryAt(HashEntry<K,V>[] tab, int i) { 305 return (tab == null) ? null : 306 (HashEntry<K,V>) UNSAFE.getObjectVolatile 307 (tab, ((long)i << TSHIFT) + TBASE); 308 } 309 310 /** 311 * Sets the ith element of given table, with volatile write 312 * semantics. (See above about use of putOrderedObject.) 313 */ 314 static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i, 315 HashEntry<K,V> e) { 316 UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e); 317 } 318 319 /** 320 * Applies a supplemental hash function to a given hashCode, which 321 * defends against poor quality hash functions. This is critical 322 * because ConcurrentHashMap uses power-of-two length hash tables, 323 * that otherwise encounter collisions for hashCodes that do not 324 * differ in lower or upper bits. 325 */ 326 private int hash(Object k) { 327 int h = hashSeed; 328 329 if ((0 != h) && (k instanceof String)) { 330 return sun.misc.Hashing.stringHash32((String) k); 331 } 332 333 h ^= k.hashCode(); 334 335 // Spread bits to regularize both segment and index locations, 336 // using variant of single-word Wang/Jenkins hash. 337 h += (h << 15) ^ 0xffffcd7d; 338 h ^= (h >>> 10); 339 h += (h << 3); 340 h ^= (h >>> 6); 341 h += (h << 2) + (h << 14); 342 return h ^ (h >>> 16); 343 } 344 345 /** 346 * Segments are specialized versions of hash tables. This 347 * subclasses from ReentrantLock opportunistically, just to 348 * simplify some locking and avoid separate construction. 349 */ 350 static final class Segment<K,V> extends ReentrantLock implements Serializable { 351 /* 352 * Segments maintain a table of entry lists that are always 353 * kept in a consistent state, so can be read (via volatile 354 * reads of segments and tables) without locking. This 355 * requires replicating nodes when necessary during table 356 * resizing, so the old lists can be traversed by readers 357 * still using old version of table. 358 * 359 * This class defines only mutative methods requiring locking. 360 * Except as noted, the methods of this class perform the 361 * per-segment versions of ConcurrentHashMap methods. (Other 362 * methods are integrated directly into ConcurrentHashMap 363 * methods.) These mutative methods use a form of controlled 364 * spinning on contention via methods scanAndLock and 365 * scanAndLockForPut. These intersperse tryLocks with 366 * traversals to locate nodes. The main benefit is to absorb 367 * cache misses (which are very common for hash tables) while 368 * obtaining locks so that traversal is faster once 369 * acquired. We do not actually use the found nodes since they 370 * must be re-acquired under lock anyway to ensure sequential 371 * consistency of updates (and in any case may be undetectably 372 * stale), but they will normally be much faster to re-locate. 373 * Also, scanAndLockForPut speculatively creates a fresh node 374 * to use in put if no node is found. 375 */ 376 377 private static final long serialVersionUID = 2249069246763182397L; 378 379 /** 380 * The maximum number of times to tryLock in a prescan before 381 * possibly blocking on acquire in preparation for a locked 382 * segment operation. On multiprocessors, using a bounded 383 * number of retries maintains cache acquired while locating 384 * nodes. 385 */ 386 static final int MAX_SCAN_RETRIES = 387 Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; 388 389 /** 390 * The per-segment table. Elements are accessed via 391 * entryAt/setEntryAt providing volatile semantics. 392 */ 393 transient volatile HashEntry<K,V>[] table; 394 395 /** 396 * The number of elements. Accessed only either within locks 397 * or among other volatile reads that maintain visibility. 398 */ 399 transient int count; 400 401 /** 402 * The total number of mutative operations in this segment. 403 * Even though this may overflows 32 bits, it provides 404 * sufficient accuracy for stability checks in CHM isEmpty() 405 * and size() methods. Accessed only either within locks or 406 * among other volatile reads that maintain visibility. 407 */ 408 transient int modCount; 409 410 /** 411 * The table is rehashed when its size exceeds this threshold. 412 * (The value of this field is always <tt>(int)(capacity * 413 * loadFactor)</tt>.) 414 */ 415 transient int threshold; 416 417 /** 418 * The load factor for the hash table. Even though this value 419 * is same for all segments, it is replicated to avoid needing 420 * links to outer object. 421 * @serial 422 */ 423 final float loadFactor; 424 425 Segment(float lf, int threshold, HashEntry<K,V>[] tab) { 426 this.loadFactor = lf; 427 this.threshold = threshold; 428 this.table = tab; 429 } 430 431 final V put(K key, int hash, V value, boolean onlyIfAbsent) { 432 HashEntry<K,V> node = tryLock() ? null : 433 scanAndLockForPut(key, hash, value); 434 V oldValue; 435 try { 436 HashEntry<K,V>[] tab = table; 437 int index = (tab.length - 1) & hash; 438 HashEntry<K,V> first = entryAt(tab, index); 439 for (HashEntry<K,V> e = first;;) { 440 if (e != null) { 441 K k; 442 if ((k = e.key) == key || 443 (e.hash == hash && key.equals(k))) { 444 oldValue = e.value; 445 if (!onlyIfAbsent) { 446 e.value = value; 447 ++modCount; 448 } 449 break; 450 } 451 e = e.next; 452 } 453 else { 454 if (node != null) 455 node.setNext(first); 456 else 457 node = new HashEntry<K,V>(hash, key, value, first); 458 int c = count + 1; 459 if (c > threshold && tab.length < MAXIMUM_CAPACITY) 460 rehash(node); 461 else 462 setEntryAt(tab, index, node); 463 ++modCount; 464 count = c; 465 oldValue = null; 466 break; 467 } 468 } 469 } finally { 470 unlock(); 471 } 472 return oldValue; 473 } 474 475 /** 476 * Doubles size of table and repacks entries, also adding the 477 * given node to new table 478 */ 479 @SuppressWarnings("unchecked") 480 private void rehash(HashEntry<K,V> node) { 481 /* 482 * Reclassify nodes in each list to new table. Because we 483 * are using power-of-two expansion, the elements from 484 * each bin must either stay at same index, or move with a 485 * power of two offset. We eliminate unnecessary node 486 * creation by catching cases where old nodes can be 487 * reused because their next fields won‘t change. 488 * Statistically, at the default threshold, only about 489 * one-sixth of them need cloning when a table 490 * doubles. The nodes they replace will be garbage 491 * collectable as soon as they are no longer referenced by 492 * any reader thread that may be in the midst of 493 * concurrently traversing table. Entry accesses use plain 494 * array indexing because they are followed by volatile 495 * table write. 496 */ 497 HashEntry<K,V>[] oldTable = table; 498 int oldCapacity = oldTable.length; 499 int newCapacity = oldCapacity << 1; 500 threshold = (int)(newCapacity * loadFactor); 501 HashEntry<K,V>[] newTable = 502 (HashEntry<K,V>[]) new HashEntry[newCapacity]; 503 int sizeMask = newCapacity - 1; 504 for (int i = 0; i < oldCapacity ; i++) { 505 HashEntry<K,V> e = oldTable[i]; 506 if (e != null) { 507 HashEntry<K,V> next = e.next; 508 int idx = e.hash & sizeMask; 509 if (next == null) // Single node on list 510 newTable[idx] = e; 511 else { // Reuse consecutive sequence at same slot 512 HashEntry<K,V> lastRun = e; 513 int lastIdx = idx; 514 for (HashEntry<K,V> last = next; 515 last != null; 516 last = last.next) { 517 int k = last.hash & sizeMask; 518 if (k != lastIdx) { 519 lastIdx = k; 520 lastRun = last; 521 } 522 } 523 newTable[lastIdx] = lastRun; 524 // Clone remaining nodes 525 for (HashEntry<K,V> p = e; p != lastRun; p = p.next) { 526 V v = p.value; 527 int h = p.hash; 528 int k = h & sizeMask; 529 HashEntry<K,V> n = newTable[k]; 530 newTable[k] = new HashEntry<K,V>(h, p.key, v, n); 531 } 532 } 533 } 534 } 535 int nodeIndex = node.hash & sizeMask; // add the new node 536 node.setNext(newTable[nodeIndex]); 537 newTable[nodeIndex] = node; 538 table = newTable; 539 } 540 541 /** 542 * Scans for a node containing given key while trying to 543 * acquire lock, creating and returning one if not found. Upon 544 * return, guarantees that lock is held. UNlike in most 545 * methods, calls to method equals are not screened: Since 546 * traversal speed doesn‘t matter, we might as well help warm 547 * up the associated code and accesses as well. 548 * 549 * @return a new node if key not found, else null 550 */ 551 private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) { 552 HashEntry<K,V> first = entryForHash(this, hash); 553 HashEntry<K,V> e = first; 554 HashEntry<K,V> node = null; 555 int retries = -1; // negative while locating node 556 while (!tryLock()) { 557 HashEntry<K,V> f; // to recheck first below 558 if (retries < 0) { 559 if (e == null) { 560 if (node == null) // speculatively create node 561 node = new HashEntry<K,V>(hash, key, value, null); 562 retries = 0; 563 } 564 else if (key.equals(e.key)) 565 retries = 0; 566 else 567 e = e.next; 568 } 569 else if (++retries > MAX_SCAN_RETRIES) { 570 lock(); 571 break; 572 } 573 else if ((retries & 1) == 0 && 574 (f = entryForHash(this, hash)) != first) { 575 e = first = f; // re-traverse if entry changed 576 retries = -1; 577 } 578 } 579 return node; 580 } 581 582 /** 583 * Scans for a node containing the given key while trying to 584 * acquire lock for a remove or replace operation. Upon 585 * return, guarantees that lock is held. Note that we must 586 * lock even if the key is not found, to ensure sequential 587 * consistency of updates. 588 */ 589 private void scanAndLock(Object key, int hash) { 590 // similar to but simpler than scanAndLockForPut 591 HashEntry<K,V> first = entryForHash(this, hash); 592 HashEntry<K,V> e = first; 593 int retries = -1; 594 while (!tryLock()) { 595 HashEntry<K,V> f; 596 if (retries < 0) { 597 if (e == null || key.equals(e.key)) 598 retries = 0; 599 else 600 e = e.next; 601 } 602 else if (++retries > MAX_SCAN_RETRIES) { 603 lock(); 604 break; 605 } 606 else if ((retries & 1) == 0 && 607 (f = entryForHash(this, hash)) != first) { 608 e = first = f; 609 retries = -1; 610 } 611 } 612 } 613 614 /** 615 * Remove; match on key only if value null, else match both. 616 */ 617 final V remove(Object key, int hash, Object value) { 618 if (!tryLock()) 619 scanAndLock(key, hash); 620 V oldValue = null; 621 try { 622 HashEntry<K,V>[] tab = table; 623 int index = (tab.length - 1) & hash; 624 HashEntry<K,V> e = entryAt(tab, index); 625 HashEntry<K,V> pred = null; 626 while (e != null) { 627 K k; 628 HashEntry<K,V> next = e.next; 629 if ((k = e.key) == key || 630 (e.hash == hash && key.equals(k))) { 631 V v = e.value; 632 if (value == null || value == v || value.equals(v)) { 633 if (pred == null) 634 setEntryAt(tab, index, next); 635 else 636 pred.setNext(next); 637 ++modCount; 638 --count; 639 oldValue = v; 640 } 641 break; 642 } 643 pred = e; 644 e = next; 645 } 646 } finally { 647 unlock(); 648 } 649 return oldValue; 650 } 651 652 final boolean replace(K key, int hash, V oldValue, V newValue) { 653 if (!tryLock()) 654 scanAndLock(key, hash); 655 boolean replaced = false; 656 try { 657 HashEntry<K,V> e; 658 for (e = entryForHash(this, hash); e != null; e = e.next) { 659 K k; 660 if ((k = e.key) == key || 661 (e.hash == hash && key.equals(k))) { 662 if (oldValue.equals(e.value)) { 663 e.value = newValue; 664 ++modCount; 665 replaced = true; 666 } 667 break; 668 } 669 } 670 } finally { 671 unlock(); 672 } 673 return replaced; 674 } 675 676 final V replace(K key, int hash, V value) { 677 if (!tryLock()) 678 scanAndLock(key, hash); 679 V oldValue = null; 680 try { 681 HashEntry<K,V> e; 682 for (e = entryForHash(this, hash); e != null; e = e.next) { 683 K k; 684 if ((k = e.key) == key || 685 (e.hash == hash && key.equals(k))) { 686 oldValue = e.value; 687 e.value = value; 688 ++modCount; 689 break; 690 } 691 } 692 } finally { 693 unlock(); 694 } 695 return oldValue; 696 } 697 698 final void clear() { 699 lock(); 700 try { 701 HashEntry<K,V>[] tab = table; 702 for (int i = 0; i < tab.length ; i++) 703 setEntryAt(tab, i, null); 704 ++modCount; 705 count = 0; 706 } finally { 707 unlock(); 708 } 709 } 710 } 711 712 // Accessing segments 713 714 /** 715 * Gets the jth element of given segment array (if nonnull) with 716 * volatile element access semantics via Unsafe. (The null check 717 * can trigger harmlessly only during deserialization.) Note: 718 * because each element of segments array is set only once (using 719 * fully ordered writes), some performance-sensitive methods rely 720 * on this method only as a recheck upon null reads. 721 */ 722 @SuppressWarnings("unchecked") 723 static final <K,V> Segment<K,V> segmentAt(Segment<K,V>[] ss, int j) { 724 long u = (j << SSHIFT) + SBASE; 725 return ss == null ? null : 726 (Segment<K,V>) UNSAFE.getObjectVolatile(ss, u); 727 } 728 729 /** 730 * Returns the segment for the given index, creating it and 731 * recording in segment table (via CAS) if not already present. 732 * 733 * @param k the index 734 * @return the segment 735 */ 736 @SuppressWarnings("unchecked") 737 private Segment<K,V> ensureSegment(int k) { 738 final Segment<K,V>[] ss = this.segments; 739 long u = (k << SSHIFT) + SBASE; // raw offset 740 Segment<K,V> seg; 741 if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { 742 Segment<K,V> proto = ss[0]; // use segment 0 as prototype 743 int cap = proto.table.length; 744 float lf = proto.loadFactor; 745 int threshold = (int)(cap * lf); 746 HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap]; 747 if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) 748 == null) { // recheck 749 Segment<K,V> s = new Segment<K,V>(lf, threshold, tab); 750 while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) 751 == null) { 752 if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s)) 753 break; 754 } 755 } 756 } 757 return seg; 758 } 759 760 // Hash-based segment and entry accesses 761 762 /** 763 * Get the segment for the given hash 764 */ 765 @SuppressWarnings("unchecked") 766 private Segment<K,V> segmentForHash(int h) { 767 long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; 768 return (Segment<K,V>) UNSAFE.getObjectVolatile(segments, u); 769 } 770 771 /** 772 * Gets the table entry for the given segment and hash 773 */ 774 @SuppressWarnings("unchecked") 775 static final <K,V> HashEntry<K,V> entryForHash(Segment<K,V> seg, int h) { 776 HashEntry<K,V>[] tab; 777 return (seg == null || (tab = seg.table) == null) ? null : 778 (HashEntry<K,V>) UNSAFE.getObjectVolatile 779 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); 780 } 781 782 /* ---------------- Public operations -------------- */ 783 784 /** 785 * Creates a new, empty map with the specified initial 786 * capacity, load factor and concurrency level. 787 * 788 * @param initialCapacity the initial capacity. The implementation 789 * performs internal sizing to accommodate this many elements. 790 * @param loadFactor the load factor threshold, used to control resizing. 791 * Resizing may be performed when the average number of elements per 792 * bin exceeds this threshold. 793 * @param concurrencyLevel the estimated number of concurrently 794 * updating threads. The implementation performs internal sizing 795 * to try to accommodate this many threads. 796 * @throws IllegalArgumentException if the initial capacity is 797 * negative or the load factor or concurrencyLevel are 798 * nonpositive. 799 */ 800 @SuppressWarnings("unchecked") 801 public ConcurrentHashMap(int initialCapacity, 802 float loadFactor, int concurrencyLevel) { 803 if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) 804 throw new IllegalArgumentException(); 805 if (concurrencyLevel > MAX_SEGMENTS) 806 concurrencyLevel = MAX_SEGMENTS; 807 // Find power-of-two sizes best matching arguments 808 int sshift = 0; 809 int ssize = 1; 810 while (ssize < concurrencyLevel) { 811 ++sshift; 812 ssize <<= 1; 813 } 814 this.segmentShift = 32 - sshift; 815 this.segmentMask = ssize - 1; 816 if (initialCapacity > MAXIMUM_CAPACITY) 817 initialCapacity = MAXIMUM_CAPACITY; 818 int c = initialCapacity / ssize; 819 if (c * ssize < initialCapacity) 820 ++c; 821 int cap = MIN_SEGMENT_TABLE_CAPACITY; 822 while (cap < c) 823 cap <<= 1; 824 // create segments and segments[0] 825 Segment<K,V> s0 = 826 new Segment<K,V>(loadFactor, (int)(cap * loadFactor), 827 (HashEntry<K,V>[])new HashEntry[cap]); 828 Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize]; 829 UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0] 830 this.segments = ss; 831 } 832 833 /** 834 * Creates a new, empty map with the specified initial capacity 835 * and load factor and with the default concurrencyLevel (16). 836 * 837 * @param initialCapacity The implementation performs internal 838 * sizing to accommodate this many elements. 839 * @param loadFactor the load factor threshold, used to control resizing. 840 * Resizing may be performed when the average number of elements per 841 * bin exceeds this threshold. 842 * @throws IllegalArgumentException if the initial capacity of 843 * elements is negative or the load factor is nonpositive 844 * 845 * @since 1.6 846 */ 847 public ConcurrentHashMap(int initialCapacity, float loadFactor) { 848 this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL); 849 } 850 851 /** 852 * Creates a new, empty map with the specified initial capacity, 853 * and with default load factor (0.75) and concurrencyLevel (16). 854 * 855 * @param initialCapacity the initial capacity. The implementation 856 * performs internal sizing to accommodate this many elements. 857 * @throws IllegalArgumentException if the initial capacity of 858 * elements is negative. 859 */ 860 public ConcurrentHashMap(int initialCapacity) { 861 this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); 862 } 863 864 /** 865 * Creates a new, empty map with a default initial capacity (16), 866 * load factor (0.75) and concurrencyLevel (16). 867 */ 868 public ConcurrentHashMap() { 869 this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); 870 } 871 872 /** 873 * Creates a new map with the same mappings as the given map. 874 * The map is created with a capacity of 1.5 times the number 875 * of mappings in the given map or 16 (whichever is greater), 876 * and a default load factor (0.75) and concurrencyLevel (16). 877 * 878 * @param m the map 879 */ 880 public ConcurrentHashMap(Map<? extends K, ? extends V> m) { 881 this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, 882 DEFAULT_INITIAL_CAPACITY), 883 DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); 884 putAll(m); 885 } 886 887 /** 888 * Returns <tt>true</tt> if this map contains no key-value mappings. 889 * 890 * @return <tt>true</tt> if this map contains no key-value mappings 891 */ 892 public boolean isEmpty() { 893 /* 894 * Sum per-segment modCounts to avoid mis-reporting when 895 * elements are concurrently added and removed in one segment 896 * while checking another, in which case the table was never 897 * actually empty at any point. (The sum ensures accuracy up 898 * through at least 1<<31 per-segment modifications before 899 * recheck.) Methods size() and containsValue() use similar 900 * constructions for stability checks. 901 */ 902 long sum = 0L; 903 final Segment<K,V>[] segments = this.segments; 904 for (int j = 0; j < segments.length; ++j) { 905 Segment<K,V> seg = segmentAt(segments, j); 906 if (seg != null) { 907 if (seg.count != 0) 908 return false; 909 sum += seg.modCount; 910 } 911 } 912 if (sum != 0L) { // recheck unless no modifications 913 for (int j = 0; j < segments.length; ++j) { 914 Segment<K,V> seg = segmentAt(segments, j); 915 if (seg != null) { 916 if (seg.count != 0) 917 return false; 918 sum -= seg.modCount; 919 } 920 } 921 if (sum != 0L) 922 return false; 923 } 924 return true; 925 } 926 927 /** 928 * Returns the number of key-value mappings in this map. If the 929 * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns 930 * <tt>Integer.MAX_VALUE</tt>. 931 * 932 * @return the number of key-value mappings in this map 933 */ 934 public int size() { 935 // Try a few times to get accurate count. On failure due to 936 // continuous async changes in table, resort to locking. 937 final Segment<K,V>[] segments = this.segments; 938 int size; 939 boolean overflow; // true if size overflows 32 bits 940 long sum; // sum of modCounts 941 long last = 0L; // previous sum 942 int retries = -1; // first iteration isn‘t retry 943 try { 944 for (;;) { 945 if (retries++ == RETRIES_BEFORE_LOCK) { 946 for (int j = 0; j < segments.length; ++j) 947 ensureSegment(j).lock(); // force creation 948 } 949 sum = 0L; 950 size = 0; 951 overflow = false; 952 for (int j = 0; j < segments.length; ++j) { 953 Segment<K,V> seg = segmentAt(segments, j); 954 if (seg != null) { 955 sum += seg.modCount; 956 int c = seg.count; 957 if (c < 0 || (size += c) < 0) 958 overflow = true; 959 } 960 } 961 if (sum == last) 962 break; 963 last = sum; 964 } 965 } finally { 966 if (retries > RETRIES_BEFORE_LOCK) { 967 for (int j = 0; j < segments.length; ++j) 968 segmentAt(segments, j).unlock(); 969 } 970 } 971 return overflow ? Integer.MAX_VALUE : size; 972 } 973 974 /** 975 * Returns the value to which the specified key is mapped, 976 * or {@code null} if this map contains no mapping for the key. 977 * 978 * <p>More formally, if this map contains a mapping from a key 979 * {@code k} to a value {@code v} such that {@code key.equals(k)}, 980 * then this method returns {@code v}; otherwise it returns 981 * {@code null}. (There can be at most one such mapping.) 982 * 983 * @throws NullPointerException if the specified key is null 984 */ 985 public V get(Object key) { 986 Segment<K,V> s; // manually integrate access methods to reduce overhead 987 HashEntry<K,V>[] tab; 988 int h = hash(key); 989 long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; 990 if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && 991 (tab = s.table) != null) { 992 for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile 993 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); 994 e != null; e = e.next) { 995 K k; 996 if ((k = e.key) == key || (e.hash == h && key.equals(k))) 997 return e.value; 998 } 999 } 1000 return null; 1001 } 1002 1003 /** 1004 * Tests if the specified object is a key in this table. 1005 * 1006 * @param key possible key 1007 * @return <tt>true</tt> if and only if the specified object 1008 * is a key in this table, as determined by the 1009 * <tt>equals</tt> method; <tt>false</tt> otherwise. 1010 * @throws NullPointerException if the specified key is null 1011 */ 1012 @SuppressWarnings("unchecked") 1013 public boolean containsKey(Object key) { 1014 Segment<K,V> s; // same as get() except no need for volatile value read 1015 HashEntry<K,V>[] tab; 1016 int h = hash(key); 1017 long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; 1018 if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && 1019 (tab = s.table) != null) { 1020 for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile 1021 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); 1022 e != null; e = e.next) { 1023 K k; 1024 if ((k = e.key) == key || (e.hash == h && key.equals(k))) 1025 return true; 1026 } 1027 } 1028 return false; 1029 } 1030 1031 /** 1032 * Returns <tt>true</tt> if this map maps one or more keys to the 1033 * specified value. Note: This method requires a full internal 1034 * traversal of the hash table, and so is much slower than 1035 * method <tt>containsKey</tt>. 1036 * 1037 * @param value value whose presence in this map is to be tested 1038 * @return <tt>true</tt> if this map maps one or more keys to the 1039 * specified value 1040 * @throws NullPointerException if the specified value is null 1041 */ 1042 public boolean containsValue(Object value) { 1043 // Same idea as size() 1044 if (value == null) 1045 throw new NullPointerException(); 1046 final Segment<K,V>[] segments = this.segments; 1047 boolean found = false; 1048 long last = 0; 1049 int retries = -1; 1050 try { 1051 outer: for (;;) { 1052 if (retries++ == RETRIES_BEFORE_LOCK) { 1053 for (int j = 0; j < segments.length; ++j) 1054 ensureSegment(j).lock(); // force creation 1055 } 1056 long hashSum = 0L; 1057 int sum = 0; 1058 for (int j = 0; j < segments.length; ++j) { 1059 HashEntry<K,V>[] tab; 1060 Segment<K,V> seg = segmentAt(segments, j); 1061 if (seg != null && (tab = seg.table) != null) { 1062 for (int i = 0 ; i < tab.length; i++) { 1063 HashEntry<K,V> e; 1064 for (e = entryAt(tab, i); e != null; e = e.next) { 1065 V v = e.value; 1066 if (v != null && value.equals(v)) { 1067 found = true; 1068 break outer; 1069 } 1070 } 1071 } 1072 sum += seg.modCount; 1073 } 1074 } 1075 if (retries > 0 && sum == last) 1076 break; 1077 last = sum; 1078 } 1079 } finally { 1080 if (retries > RETRIES_BEFORE_LOCK) { 1081 for (int j = 0; j < segments.length; ++j) 1082 segmentAt(segments, j).unlock(); 1083 } 1084 } 1085 return found; 1086 } 1087 1088 /** 1089 * Legacy method testing if some key maps into the specified value 1090 * in this table. This method is identical in functionality to 1091 * {@link #containsValue}, and exists solely to ensure 1092 * full compatibility with class {@link java.util.Hashtable}, 1093 * which supported this method prior to introduction of the 1094 * Java Collections framework. 1095 1096 * @param value a value to search for 1097 * @return <tt>true</tt> if and only if some key maps to the 1098 * <tt>value</tt> argument in this table as 1099 * determined by the <tt>equals</tt> method; 1100 * <tt>false</tt> otherwise 1101 * @throws NullPointerException if the specified value is null 1102 */ 1103 public boolean contains(Object value) { 1104 return containsValue(value); 1105 } 1106 1107 /** 1108 * Maps the specified key to the specified value in this table. 1109 * Neither the key nor the value can be null. 1110 * 1111 * <p> The value can be retrieved by calling the <tt>get</tt> method 1112 * with a key that is equal to the original key. 1113 * 1114 * @param key key with which the specified value is to be associated 1115 * @param value value to be associated with the specified key 1116 * @return the previous value associated with <tt>key</tt>, or 1117 * <tt>null</tt> if there was no mapping for <tt>key</tt> 1118 * @throws NullPointerException if the specified key or value is null 1119 */ 1120 @SuppressWarnings("unchecked") 1121 public V put(K key, V value) { 1122 Segment<K,V> s; 1123 if (value == null) 1124 throw new NullPointerException(); 1125 int hash = hash(key); 1126 int j = (hash >>> segmentShift) & segmentMask; 1127 if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck 1128 (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment 1129 s = ensureSegment(j); 1130 return s.put(key, hash, value, false); 1131 } 1132 1133 /** 1134 * {@inheritDoc} 1135 * 1136 * @return the previous value associated with the specified key, 1137 * or <tt>null</tt> if there was no mapping for the key 1138 * @throws NullPointerException if the specified key or value is null 1139 */ 1140 @SuppressWarnings("unchecked") 1141 public V putIfAbsent(K key, V value) { 1142 Segment<K,V> s; 1143 if (value == null) 1144 throw new NullPointerException(); 1145 int hash = hash(key); 1146 int j = (hash >>> segmentShift) & segmentMask; 1147 if ((s = (Segment<K,V>)UNSAFE.getObject 1148 (segments, (j << SSHIFT) + SBASE)) == null) 1149 s = ensureSegment(j); 1150 return s.put(key, hash, value, true); 1151 } 1152 1153 /** 1154 * Copies all of the mappings from the specified map to this one. 1155 * These mappings replace any mappings that this map had for any of the 1156 * keys currently in the specified map. 1157 * 1158 * @param m mappings to be stored in this map 1159 */ 1160 public void putAll(Map<? extends K, ? extends V> m) { 1161 for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) 1162 put(e.getKey(), e.getValue()); 1163 } 1164 1165 /** 1166 * Removes the key (and its corresponding value) from this map. 1167 * This method does nothing if the key is not in the map. 1168 * 1169 * @param key the key that needs to be removed 1170 * @return the previous value associated with <tt>key</tt>, or 1171 * <tt>null</tt> if there was no mapping for <tt>key</tt> 1172 * @throws NullPointerException if the specified key is null 1173 */ 1174 public V remove(Object key) { 1175 int hash = hash(key); 1176 Segment<K,V> s = segmentForHash(hash); 1177 return s == null ? null : s.remove(key, hash, null); 1178 } 1179 1180 /** 1181 * {@inheritDoc} 1182 * 1183 * @throws NullPointerException if the specified key is null 1184 */ 1185 public boolean remove(Object key, Object value) { 1186 int hash = hash(key); 1187 Segment<K,V> s; 1188 return value != null && (s = segmentForHash(hash)) != null && 1189 s.remove(key, hash, value) != null; 1190 } 1191 1192 /** 1193 * {@inheritDoc} 1194 * 1195 * @throws NullPointerException if any of the arguments are null 1196 */ 1197 public boolean replace(K key, V oldValue, V newValue) { 1198 int hash = hash(key); 1199 if (oldValue == null || newValue == null) 1200 throw new NullPointerException(); 1201 Segment<K,V> s = segmentForHash(hash); 1202 return s != null && s.replace(key, hash, oldValue, newValue); 1203 } 1204 1205 /** 1206 * {@inheritDoc} 1207 * 1208 * @return the previous value associated with the specified key, 1209 * or <tt>null</tt> if there was no mapping for the key 1210 * @throws NullPointerException if the specified key or value is null 1211 */ 1212 public V replace(K key, V value) { 1213 int hash = hash(key); 1214 if (value == null) 1215 throw new NullPointerException(); 1216 Segment<K,V> s = segmentForHash(hash); 1217 return s == null ? null : s.replace(key, hash, value); 1218 } 1219 1220 /** 1221 * Removes all of the mappings from this map. 1222 */ 1223 public void clear() { 1224 final Segment<K,V>[] segments = this.segments; 1225 for (int j = 0; j < segments.length; ++j) { 1226 Segment<K,V> s = segmentAt(segments, j); 1227 if (s != null) 1228 s.clear(); 1229 } 1230 } 1231 1232 /** 1233 * Returns a {@link Set} view of the keys contained in this map. 1234 * The set is backed by the map, so changes to the map are 1235 * reflected in the set, and vice-versa. The set supports element 1236 * removal, which removes the corresponding mapping from this map, 1237 * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, 1238 * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> 1239 * operations. It does not support the <tt>add</tt> or 1240 * <tt>addAll</tt> operations. 1241 * 1242 * <p>The view‘s <tt>iterator</tt> is a "weakly consistent" iterator 1243 * that will never throw {@link ConcurrentModificationException}, 1244 * and guarantees to traverse elements as they existed upon 1245 * construction of the iterator, and may (but is not guaranteed to) 1246 * reflect any modifications subsequent to construction. 1247 */ 1248 public Set<K> keySet() { 1249 Set<K> ks = keySet; 1250 return (ks != null) ? ks : (keySet = new KeySet()); 1251 } 1252 1253 /** 1254 * Returns a {@link Collection} view of the values contained in this map. 1255 * The collection is backed by the map, so changes to the map are 1256 * reflected in the collection, and vice-versa. The collection 1257 * supports element removal, which removes the corresponding 1258 * mapping from this map, via the <tt>Iterator.remove</tt>, 1259 * <tt>Collection.remove</tt>, <tt>removeAll</tt>, 1260 * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not 1261 * support the <tt>add</tt> or <tt>addAll</tt> operations. 1262 * 1263 * <p>The view‘s <tt>iterator</tt> is a "weakly consistent" iterator 1264 * that will never throw {@link ConcurrentModificationException}, 1265 * and guarantees to traverse elements as they existed upon 1266 * construction of the iterator, and may (but is not guaranteed to) 1267 * reflect any modifications subsequent to construction. 1268 */ 1269 public Collection<V> values() { 1270 Collection<V> vs = values; 1271 return (vs != null) ? vs : (values = new Values()); 1272 } 1273 1274 /** 1275 * Returns a {@link Set} view of the mappings contained in this map. 1276 * The set is backed by the map, so changes to the map are 1277 * reflected in the set, and vice-versa. The set supports element 1278 * removal, which removes the corresponding mapping from the map, 1279 * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, 1280 * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> 1281 * operations. It does not support the <tt>add</tt> or 1282 * <tt>addAll</tt> operations. 1283 * 1284 * <p>The view‘s <tt>iterator</tt> is a "weakly consistent" iterator 1285 * that will never throw {@link ConcurrentModificationException}, 1286 * and guarantees to traverse elements as they existed upon 1287 * construction of the iterator, and may (but is not guaranteed to) 1288 * reflect any modifications subsequent to construction. 1289 */ 1290 public Set<Map.Entry<K,V>> entrySet() { 1291 Set<Map.Entry<K,V>> es = entrySet; 1292 return (es != null) ? es : (entrySet = new EntrySet()); 1293 } 1294 1295 /** 1296 * Returns an enumeration of the keys in this table. 1297 * 1298 * @return an enumeration of the keys in this table 1299 * @see #keySet() 1300 */ 1301 public Enumeration<K> keys() { 1302 return new KeyIterator(); 1303 } 1304 1305 /** 1306 * Returns an enumeration of the values in this table. 1307 * 1308 * @return an enumeration of the values in this table 1309 * @see #values() 1310 */ 1311 public Enumeration<V> elements() { 1312 return new ValueIterator(); 1313 } 1314 1315 /* ---------------- Iterator Support -------------- */ 1316 1317 abstract class HashIterator { 1318 int nextSegmentIndex; 1319 int nextTableIndex; 1320 HashEntry<K,V>[] currentTable; 1321 HashEntry<K, V> nextEntry; 1322 HashEntry<K, V> lastReturned; 1323 1324 HashIterator() { 1325 nextSegmentIndex = segments.length - 1; 1326 nextTableIndex = -1; 1327 advance(); 1328 } 1329 1330 /** 1331 * Set nextEntry to first node of next non-empty table 1332 * (in backwards order, to simplify checks). 1333 */ 1334 final void advance() { 1335 for (;;) { 1336 if (nextTableIndex >= 0) { 1337 if ((nextEntry = entryAt(currentTable, 1338 nextTableIndex--)) != null) 1339 break; 1340 } 1341 else if (nextSegmentIndex >= 0) { 1342 Segment<K,V> seg = segmentAt(segments, nextSegmentIndex--); 1343 if (seg != null && (currentTable = seg.table) != null) 1344 nextTableIndex = currentTable.length - 1; 1345 } 1346 else 1347 break; 1348 } 1349 } 1350 1351 final HashEntry<K,V> nextEntry() { 1352 HashEntry<K,V> e = nextEntry; 1353 if (e == null) 1354 throw new NoSuchElementException(); 1355 lastReturned = e; // cannot assign until after null check 1356 if ((nextEntry = e.next) == null) 1357 advance(); 1358 return e; 1359 } 1360 1361 public final boolean hasNext() { return nextEntry != null; } 1362 public final boolean hasMoreElements() { return nextEntry != null; } 1363 1364 public final void remove() { 1365 if (lastReturned == null) 1366 throw new IllegalStateException(); 1367 ConcurrentHashMap.this.remove(lastReturned.key); 1368 lastReturned = null; 1369 } 1370 } 1371 1372 final class KeyIterator 1373 extends HashIterator 1374 implements Iterator<K>, Enumeration<K> 1375 { 1376 public final K next() { return super.nextEntry().key; } 1377 public final K nextElement() { return super.nextEntry().key; } 1378 } 1379 1380 final class ValueIterator 1381 extends HashIterator 1382 implements Iterator<V>, Enumeration<V> 1383 { 1384 public final V next() { return super.nextEntry().value; } 1385 public final V nextElement() { return super.nextEntry().value; } 1386 } 1387 1388 /** 1389 * Custom Entry class used by EntryIterator.next(), that relays 1390 * setValue changes to the underlying map. 1391 */ 1392 final class WriteThroughEntry 1393 extends AbstractMap.SimpleEntry<K,V> 1394 { 1395 WriteThroughEntry(K k, V v) { 1396 super(k,v); 1397 } 1398 1399 /** 1400 * Set our entry‘s value and write through to the map. The 1401 * value to return is somewhat arbitrary here. Since a 1402 * WriteThroughEntry does not necessarily track asynchronous 1403 * changes, the most recent "previous" value could be 1404 * different from what we return (or could even have been 1405 * removed in which case the put will re-establish). We do not 1406 * and cannot guarantee more. 1407 */ 1408 public V setValue(V value) { 1409 if (value == null) throw new NullPointerException(); 1410 V v = super.setValue(value); 1411 ConcurrentHashMap.this.put(getKey(), value); 1412 return v; 1413 } 1414 } 1415 1416 final class EntryIterator 1417 extends HashIterator 1418 implements Iterator<Entry<K,V>> 1419 { 1420 public Map.Entry<K,V> next() { 1421 HashEntry<K,V> e = super.nextEntry(); 1422 return new WriteThroughEntry(e.key, e.value); 1423 } 1424 } 1425 1426 final class KeySet extends AbstractSet<K> { 1427 public Iterator<K> iterator() { 1428 return new KeyIterator(); 1429 } 1430 public int size() { 1431 return ConcurrentHashMap.this.size(); 1432 } 1433 public boolean isEmpty() { 1434 return ConcurrentHashMap.this.isEmpty(); 1435 } 1436 public boolean contains(Object o) { 1437 return ConcurrentHashMap.this.containsKey(o); 1438 } 1439 public boolean remove(Object o) { 1440 return ConcurrentHashMap.this.remove(o) != null; 1441 } 1442 public void clear() { 1443 ConcurrentHashMap.this.clear(); 1444 } 1445 } 1446 1447 final class Values extends AbstractCollection<V> { 1448 public Iterator<V> iterator() { 1449 return new ValueIterator(); 1450 } 1451 public int size() { 1452 return ConcurrentHashMap.this.size(); 1453 } 1454 public boolean isEmpty() { 1455 return ConcurrentHashMap.this.isEmpty(); 1456 } 1457 public boolean contains(Object o) { 1458 return ConcurrentHashMap.this.containsValue(o); 1459 } 1460 public void clear() { 1461 ConcurrentHashMap.this.clear(); 1462 } 1463 } 1464 1465 final class EntrySet extends AbstractSet<Map.Entry<K,V>> { 1466 public Iterator<Map.Entry<K,V>> iterator() { 1467 return new EntryIterator(); 1468 } 1469 public boolean contains(Object o) { 1470 if (!(o instanceof Map.Entry)) 1471 return false; 1472 Map.Entry<?,?> e = (Map.Entry<?,?>)o; 1473 V v = ConcurrentHashMap.this.get(e.getKey()); 1474 return v != null && v.equals(e.getValue()); 1475 } 1476 public boolean remove(Object o) { 1477 if (!(o instanceof Map.Entry)) 1478 return false; 1479 Map.Entry<?,?> e = (Map.Entry<?,?>)o; 1480 return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()); 1481 } 1482 public int size() { 1483 return ConcurrentHashMap.this.size(); 1484 } 1485 public boolean isEmpty() { 1486 return ConcurrentHashMap.this.isEmpty(); 1487 } 1488 public void clear() { 1489 ConcurrentHashMap.this.clear(); 1490 } 1491 } 1492 1493 /* ---------------- Serialization Support -------------- */ 1494 1495 /** 1496 * Save the state of the <tt>ConcurrentHashMap</tt> instance to a 1497 * stream (i.e., serialize it). 1498 * @param s the stream 1499 * @serialData 1500 * the key (Object) and value (Object) 1501 * for each key-value mapping, followed by a null pair. 1502 * The key-value mappings are emitted in no particular order. 1503 */ 1504 private void writeObject(java.io.ObjectOutputStream s) throws IOException { 1505 // force all segments for serialization compatibility 1506 for (int k = 0; k < segments.length; ++k) 1507 ensureSegment(k); 1508 s.defaultWriteObject(); 1509 1510 final Segment<K,V>[] segments = this.segments; 1511 for (int k = 0; k < segments.length; ++k) { 1512 Segment<K,V> seg = segmentAt(segments, k); 1513 seg.lock(); 1514 try { 1515 HashEntry<K,V>[] tab = seg.table; 1516 for (int i = 0; i < tab.length; ++i) { 1517 HashEntry<K,V> e; 1518 for (e = entryAt(tab, i); e != null; e = e.next) { 1519 s.writeObject(e.key); 1520 s.writeObject(e.value); 1521 } 1522 } 1523 } finally { 1524 seg.unlock(); 1525 } 1526 } 1527 s.writeObject(null); 1528 s.writeObject(null); 1529 } 1530 1531 /** 1532 * Reconstitute the <tt>ConcurrentHashMap</tt> instance from a 1533 * stream (i.e., deserialize it). 1534 * @param s the stream 1535 */ 1536 @SuppressWarnings("unchecked") 1537 private void readObject(java.io.ObjectInputStream s) 1538 throws IOException, ClassNotFoundException { 1539 // Don‘t call defaultReadObject() 1540 ObjectInputStream.GetField oisFields = s.readFields(); 1541 final Segment<K,V>[] oisSegments = (Segment<K,V>[])oisFields.get("segments", null); 1542 1543 final int ssize = oisSegments.length; 1544 if (ssize < 1 || ssize > MAX_SEGMENTS 1545 || (ssize & (ssize-1)) != 0 ) // ssize not power of two 1546 throw new java.io.InvalidObjectException("Bad number of segments:" 1547 + ssize); 1548 int sshift = 0, ssizeTmp = ssize; 1549 while (ssizeTmp > 1) { 1550 ++sshift; 1551 ssizeTmp >>>= 1; 1552 } 1553 UNSAFE.putIntVolatile(this, SEGSHIFT_OFFSET, 32 - sshift); 1554 UNSAFE.putIntVolatile(this, SEGMASK_OFFSET, ssize - 1); 1555 UNSAFE.putObjectVolatile(this, SEGMENTS_OFFSET, oisSegments); 1556 1557 // set hashMask 1558 UNSAFE.putIntVolatile(this, HASHSEED_OFFSET, randomHashSeed(this)); 1559 1560 // Re-initialize segments to be minimally sized, and let grow. 1561 int cap = MIN_SEGMENT_TABLE_CAPACITY; 1562 final Segment<K,V>[] segments = this.segments; 1563 for (int k = 0; k < segments.length; ++k) { 1564 Segment<K,V> seg = segments[k]; 1565 if (seg != null) { 1566 seg.threshold = (int)(cap * seg.loadFactor); 1567 seg.table = (HashEntry<K,V>[]) new HashEntry[cap]; 1568 } 1569 } 1570 1571 // Read the keys and values, and put the mappings in the table 1572 for (;;) { 1573 K key = (K) s.readObject(); 1574 V value = (V) s.readObject(); 1575 if (key == null) 1576 break; 1577 put(key, value); 1578 } 1579 } 1580 1581 // Unsafe mechanics 1582 private static final sun.misc.Unsafe UNSAFE; 1583 private static final long SBASE; 1584 private static final int SSHIFT; 1585 private static final long TBASE; 1586 private static final int TSHIFT; 1587 private static final long HASHSEED_OFFSET; 1588 private static final long SEGSHIFT_OFFSET; 1589 private static final long SEGMASK_OFFSET; 1590 private static final long SEGMENTS_OFFSET; 1591 1592 static { 1593 int ss, ts; 1594 try { 1595 UNSAFE = sun.misc.Unsafe.getUnsafe(); 1596 Class tc = HashEntry[].class; 1597 Class sc = Segment[].class; 1598 TBASE = UNSAFE.arrayBaseOffset(tc); 1599 SBASE = UNSAFE.arrayBaseOffset(sc); 1600 ts = UNSAFE.arrayIndexScale(tc); 1601 ss = UNSAFE.arrayIndexScale(sc); 1602 HASHSEED_OFFSET = UNSAFE.objectFieldOffset( 1603 ConcurrentHashMap.class.getDeclaredField("hashSeed")); 1604 SEGSHIFT_OFFSET = UNSAFE.objectFieldOffset( 1605 ConcurrentHashMap.class.getDeclaredField("segmentShift")); 1606 SEGMASK_OFFSET = UNSAFE.objectFieldOffset( 1607 ConcurrentHashMap.class.getDeclaredField("segmentMask")); 1608 SEGMENTS_OFFSET = UNSAFE.objectFieldOffset( 1609 ConcurrentHashMap.class.getDeclaredField("segments")); 1610 } catch (Exception e) { 1611 throw new Error(e); 1612 } 1613 if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0) 1614 throw new Error("data type scale not a power of two"); 1615 SSHIFT = 31 - Integer.numberOfLeadingZeros(ss); 1616 TSHIFT = 31 - Integer.numberOfLeadingZeros(ts); 1617 } 1618 1619 }
下面从ConcurrentHashMap的创建,获取,添加,删除这4个方面对ConcurrentHashMap进行分析。
1 创建
下面以ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel)来进行说明。
@SuppressWarnings("unchecked") public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { // 参数有效性判断 if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) throw new IllegalArgumentException(); // concurrencyLevel是“用来计算segments的容量” if (concurrencyLevel > MAX_SEGMENTS) concurrencyLevel = MAX_SEGMENTS; int sshift = 0; int ssize = 1; // ssize=“大于或等于concurrencyLevel的最小的2的N次方值” while (ssize < concurrencyLevel) { ++sshift; ssize <<= 1; } // 初始化segmentShift和segmentMask this.segmentShift = 32 - sshift; this.segmentMask = ssize - 1; // 哈希表的初始容量 // 哈希表的实际容量=“segments的容量” x “segments中数组的长度” if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; // “哈希表的初始容量” / “segments的容量” int c = initialCapacity / ssize; if (c * ssize < initialCapacity) ++c; // cap就是“segments中的HashEntry数组的长度” int cap = MIN_SEGMENT_TABLE_CAPACITY; while (cap < c) cap <<= 1; // segments Segment<K,V> s0 = new Segment<K,V>(loadFactor, (int)(cap * loadFactor), (HashEntry<K,V>[])new HashEntry[cap]); Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize]; UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0] this.segments = ss; }
说明:
(01)
前面我们说过,ConcurrentHashMap采用了“锁分段”技术;在代码中,它通过“segments数组”对象来保存各个分段。segments的定义如下:
final Segment<K,V>[] segments;
concurrencyLevel的作用就是用来计算segments数组的容量大小。先计算出“大于或等于concurrencyLevel的最小的2的N次方值”,然后将其保存为“segments的容量大小(ssize)”。
(02)
initialCapacity是哈希表的初始容量。需要注意的是,哈希表的实际容量=“segments的容量” x
“segments中数组的长度”。
(03)
loadFactor是加载因子。它是哈希表在其容量自动增加之前可以达到多满的一种尺度。
ConcurrentHashMap的构造函数中涉及到的非常重要的一个结构体,它就是Segment。下面看看Segment的声明:
static final class Segment<K,V> extends ReentrantLock implements Serializable { ... transient volatile HashEntry<K,V>[] table; // threshold阈,是哈希表在其容量自动增加之前可以达到多满的一种尺度。 transient int threshold; // loadFactor是加载因子 final float loadFactor; Segment(float lf, int threshold, HashEntry<K,V>[] tab) { this.loadFactor = lf; this.threshold = threshold; this.table = tab; } ... }
说明:Segment包含HashEntry数组,HashEntry保存了哈希表中的键值对。
此外,还需要说明的Segment继承于ReentrantLock。这意味着,Segment本质上就是可重入的互斥锁。
HashEntry的源码如下:
static final class HashEntry<K,V> { final int hash; // 哈希值 final K key; // 键 volatile V value; // 值 volatile HashEntry<K,V> next; // 下一个HashEntry节点 HashEntry(int hash, K key, V value, HashEntry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } ... }
说明:和HashMap的节点一样,HashEntry也是链表。这就说明,ConcurrentHashMap是链式哈希表,它是通过“拉链法”来解决哈希冲突的。
2 获取
下面以get(Object key)为例,对ConcurrentHashMap的获取方法进行说明。
public V get(Object key) { Segment<K,V> s; // manually integrate access methods to reduce overhead HashEntry<K,V>[] tab; int h = hash(key); long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; // 获取key对应的Segment片段。 // 如果Segment片段不为null,则在“Segment片段的HashEntry数组中”中找到key所对应的HashEntry列表; // 接着遍历该HashEntry链表,找到于key-value键值对对应的HashEntry节点。 if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) { for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); e != null; e = e.next) { K k; if ((k = e.key) == key || (e.hash == h && key.equals(k))) return e.value; } } return null; }
说明:get(Object
key)的作用是返回key在ConcurrentHashMap哈希表中对应的值。
它首先根据key计算出来的哈希值,获取key所对应的Segment片段。
如果Segment片段不为null,则在“Segment片段的HashEntry数组中”中找到key所对应的HashEntry列表。Segment包含“HashEntry数组”对象,而每一个HashEntry本质上是一个单向链表。
接着遍历该HashEntry链表,找到于key-value键值对对应的HashEntry节点。
下面是hash()的源码
private int hash(Object k) { int h = hashSeed; if ((0 != h) && (k instanceof String)) { return sun.misc.Hashing.stringHash32((String) k); } h ^= k.hashCode(); // 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); }
3 增加
下面以put(K key, V value)来对ConcurrentHashMap中增加键值对来进行说明。
public V put(K key, V value) { Segment<K,V> s; if (value == null) throw new NullPointerException(); // 获取key对应的哈希值 int hash = hash(key); int j = (hash >>> segmentShift) & segmentMask; // 如果找不到该Segment,则新建一个。 if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment s = ensureSegment(j); return s.put(key, hash, value, false); }
说明:
(01)
put()根据key获取对应的哈希值,再根据哈希值找到对应的Segment片段。如果Segment片段不存在,则新增一个Segment。
(02) 将key-value键值对添加到Segment片段中。
final V put(K key, int hash, V value, boolean onlyIfAbsent) { // tryLock()获取锁,成功返回true,失败返回false。 // 获取锁失败的话,则通过scanAndLockForPut()获取锁,并返回”要插入的key-value“对应的”HashEntry链表“。 HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value); V oldValue; try { // tab代表”当前Segment中的HashEntry数组“ HashEntry<K,V>[] tab = table; // 根据”hash值“获取”HashEntry数组中对应的HashEntry链表“ int index = (tab.length - 1) & hash; HashEntry<K,V> first = entryAt(tab, index); for (HashEntry<K,V> e = first;;) { // 如果”HashEntry链表中的当前HashEntry节点“不为null, if (e != null) { K k; // 当”要插入的key-value键值对“已经存在于”HashEntry链表中“时,先保存原有的值。 // 若”onlyIfAbsent“为true,即”要插入的key不存在时才插入”,则直接退出; // 否则,用新的value值覆盖原有的原有的值。 if ((k = e.key) == key || (e.hash == hash && key.equals(k))) { oldValue = e.value; if (!onlyIfAbsent) { e.value = value; ++modCount; } break; } e = e.next; } else { // 如果node非空,则将first设置为“node的下一个节点”。 // 否则,新建HashEntry链表 if (node != null) node.setNext(first); else node = new HashEntry<K,V>(hash, key, value, first); int c = count + 1; // 如果添加key-value键值对之后,Segment中的元素超过阈值(并且,HashEntry数组的长度没超过限制),则rehash; // 否则,直接添加key-value键值对。 if (c > threshold && tab.length < MAXIMUM_CAPACITY) rehash(node); else setEntryAt(tab, index, node); ++modCount; count = c; oldValue = null; break; } } } finally { // 释放锁 unlock(); } return oldValue; }
说明:
put()的作用是将key-value键值对插入到“当前Segment对应的HashEntry中”,在插入前它会获取Segment对应的互斥锁,插入后会释放锁。具体的插入过程如下:
(01)
首先根据“hash值”获取“当前Segment的HashEntry数组对象”中的“HashEntry节点”,每个HashEntry节点都是一个单向链表。
(02) 接着,遍历HashEntry链表。
若在遍历HashEntry链表时,找到与“要key-value键值对”对应的节点,即“要插入的key-value键值对”的key已经存在于HashEntry链表中。则根据onlyIfAbsent进行判断,若onlyIfAbsent为true,即“当要插入的key不存在时才插入”,则不进行插入,直接返回;否则,用新的value值覆盖原始的value值,然后再返回。
若在遍历HashEntry链表时,没有找到与“要key-value键值对”对应的节点。当node!=null时,即在scanAndLockForPut()获取锁时,已经新建了key-value对应的HashEntry节点,则”将HashEntry添加到Segment中“;否则,新建key-value对应的HashEntry节点,然后再“将HashEntry添加到Segment中”。
在”将HashEntry添加到Segment中“前,会判断是否需要rehash。如果在添加key-value键值之后,容量会超过阈值,并且HashEntry数组的长度没有超过限制,则进行rehash;否则,直接通过setEntryAt()将key-value键值对添加到Segment中。
在介绍rehash()和setEntryAt()之前,我们先看看自旋函数scanAndLockForPut()。下面是它的源码:
private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) { // 第一个HashEntry节点 HashEntry<K,V> first = entryForHash(this, hash); // 当前的HashEntry节点 HashEntry<K,V> e = first; HashEntry<K,V> node = null; // 重复计数(自旋计数器) int retries = -1; // negative while locating node // 查找”key-value键值对“在”HashEntry链表上对应的节点“; // 若找到的话,则不断的自旋;在自旋期间,若通过tryLock()获取锁成功则返回;否则自旋MAX_SCAN_RETRIES次数之后,强制获取”锁“并退出。 // 若没有找到的话,则新建一个HashEntry链表。然后不断的自旋。 // 此外,若在自旋期间,HashEntry链表的表头发生变化;则重新进行查找和自旋工作! while (!tryLock()) { HashEntry<K,V> f; // to recheck first below // 1. retries<0的处理情况 if (retries < 0) { // 1.1 如果当前的HashEntry节点为空(意味着,在该HashEntry链表上上没有找到”要插入的键值对“对应的节点),而且node=null;则新建HashEntry链表。 if (e == null) { if (node == null) // speculatively create node node = new HashEntry<K,V>(hash, key, value, null); retries = 0; } // 1.2 如果当前的HashEntry节点是”要插入的键值对在该HashEntry上对应的节点“,则设置retries=0 else if (key.equals(e.key)) retries = 0; // 1.3 设置为下一个HashEntry。 else e = e.next; } // 2. 如果自旋次数超过限制,则获取“锁”并退出 else if (++retries > MAX_SCAN_RETRIES) { lock(); break; } // 3. 当“尝试了偶数次”时,就获取“当前Segment的第一个HashEntry”,即f。 // 然后,通过f!=first来判断“当前Segment的第一个HashEntry是否发生了改变”。 // 若是的话,则重置e,first和retries的值,并重新遍历。 else if ((retries & 1) == 0 && (f = entryForHash(this, hash)) != first) { e = first = f; // re-traverse if entry changed retries = -1; } } return node; }
说明:
scanAndLockForPut()的目标是获取锁。流程如下:
它首先会调用entryForHash(),根据hash值获取”当前Segment中对应的HashEntry节点(first),即找到对应的HashEntry链表“。
紧接着进入while循环。在while循环中,它会遍历”HashEntry链表(e)“,查找”要插入的key-value键值对“在”该HashEntry链表上对应的节点“。
若找到的话,则不断的自旋,即不断的执行while循环。在自旋期间,若通过tryLock()获取锁成功则返回;否则,在自旋MAX_SCAN_RETRIES次数之后,强制获取锁并退出。
若没有找到的话,则新建一个HashEntry链表,然后不断的自旋。在自旋期间,若通过tryLock()获取锁成功则返回;否则,在自旋MAX_SCAN_RETRIES次数之后,强制获取锁并退出。
此外,若在自旋期间,HashEntry链表的表头发生变化;则重新进行查找和自旋工作!
理解scanAndLockForPut()时,务必要联系”哈希表“的数据结构。一个Segment本身就是一个哈希表,Segment中包含了”HashEntry数组“对象,而每一个HashEntry对象本身是一个”单向链表“。
下面看看rehash()的实现代码。
private void rehash(HashEntry<K,V> node) { HashEntry<K,V>[] oldTable = table; // ”Segment中原始的HashEntry数组的长度“ int oldCapacity = oldTable.length; // ”Segment中新HashEntry数组的长度“ int newCapacity = oldCapacity << 1; // 新的阈值 threshold = (int)(newCapacity * loadFactor); // 新的HashEntry数组 HashEntry<K,V>[] newTable = (HashEntry<K,V>[]) new HashEntry[newCapacity]; int sizeMask = newCapacity - 1; // 遍历”原始的HashEntry数组“, // 将”原始的HashEntry数组“中的每个”HashEntry链表“的值,都复制到”新的HashEntry数组的HashEntry元素“中。 for (int i = 0; i < oldCapacity ; i++) { // 获取”原始的HashEntry数组“中的”第i个HashEntry链表“ HashEntry<K,V> e = oldTable[i]; if (e != null) { HashEntry<K,V> next = e.next; int idx = e.hash & sizeMask; if (next == null) // Single node on list newTable[idx] = e; else { // Reuse consecutive sequence at same slot HashEntry<K,V> lastRun = e; int lastIdx = idx; for (HashEntry<K,V> last = next; last != null; last = last.next) { int k = last.hash & sizeMask; if (k != lastIdx) { lastIdx = k; lastRun = last; } } newTable[lastIdx] = lastRun; // 将”原始的HashEntry数组“中的”HashEntry链表(e)“的值,都复制到”新的HashEntry数组的HashEntry“中。 for (HashEntry<K,V> p = e; p != lastRun; p = p.next) { V v = p.value; int h = p.hash; int k = h & sizeMask; HashEntry<K,V> n = newTable[k]; newTable[k] = new HashEntry<K,V>(h, p.key, v, n); } } } } // 将新的node节点添加到“Segment的新HashEntry数组(newTable)“中。 int nodeIndex = node.hash & sizeMask; // add the new node node.setNext(newTable[nodeIndex]); newTable[nodeIndex] = node; table = newTable; }
说明:rehash()的作用是将”Segment的容量“变为”原始的Segment容量的2倍“。
在将原始的数据拷贝到“新的Segment”中后,会将新增加的key-value键值对添加到“新的Segment”中。
setEntryAt()的源码如下:
static final <K,V> void setEntryAt(HashEntry<K,V>[] tab, int i, HashEntry<K,V> e) { UNSAFE.putOrderedObject(tab, ((long)i << TSHIFT) + TBASE, e); }
UNSAFE是Segment类中定义的“静态sun.misc.Unsafe”对象。源码如下:
static final sun.misc.Unsafe UNSAFE;
Unsafe.java在openjdk6中的路径是:openjdk6/jdk/src/share/classes/sun/misc/Unsafe.java。其中,putOrderedObject()的源码下:
public native void putOrderedObject(Object o, long offset, Object x);
说明:putOrderedObject()是一个本地方法。
它会设置obj对象中offset偏移地址对应的object型field的值为指定值。它是一个有序或者有延迟的putObjectVolatile()方法,并且不保证值的改变被其他线程立即看到。只有在field被volatile修饰并且期望被意外修改的时候,使用putOrderedObject()才有用。
总之,setEntryAt()的目的是设置tab中第i位置元素的值为e,且该设置会有延迟。
4 删除
下面以remove(Object key)来对ConcurrentHashMap中的删除操作来进行说明。
public V remove(Object key) { int hash = hash(key); // 根据hash值,找到key对应的Segment片段。 Segment<K,V> s = segmentForHash(hash); return s == null ? null : s.remove(key, hash, null); }
说明:remove()首先根据“key的计算出来的哈希值”找到对应的Segment片段,然后再从该Segment片段中删除对应的“key-value键值对”。
remove()的方法如下:
final V remove(Object key, int hash, Object value) { // 尝试获取Segment对应的锁。 // 尝试失败的话,则通过scanAndLock()来获取锁。 if (!tryLock()) scanAndLock(key, hash); V oldValue = null; try { // 根据“hash值”找到“Segment的HashEntry数组”中对应的“HashEntry节点(e)”,该HashEntry节点是一HashEntry个链表。 HashEntry<K,V>[] tab = table; int index = (tab.length - 1) & hash; HashEntry<K,V> e = entryAt(tab, index); HashEntry<K,V> pred = null; // 遍历“HashEntry链表”,删除key-value键值对 while (e != null) { K k; HashEntry<K,V> next = e.next; if ((k = e.key) == key || (e.hash == hash && key.equals(k))) { V v = e.value; if (value == null || value == v || value.equals(v)) { if (pred == null) setEntryAt(tab, index, next); else pred.setNext(next); ++modCount; --count; oldValue = v; } break; } pred = e; e = next; } } finally { // 释放锁 unlock(); } return oldValue; }
说明:remove()的目的就是删除key-value键值对。在删除之前,它会获取到Segment的互斥锁,在删除之后,再释放锁。
它的删除过程也比较简单,它会先根据hash值,找到“Segment的HashEntry数组”中对应的“HashEntry”节点。根据Segment的数据结构,我们知道Segment中包含一个HashEntry数组对象,而每一个HashEntry本质上是一个单向链表。
在找到“HashEntry”节点之后,就遍历该“HashEntry”节点对应的链表,找到key-value键值对对应的节点,然后删除。
下面对scanAndLock()进行说明。它的源码如下:
private void scanAndLock(Object key, int hash) { // 第一个HashEntry节点 HashEntry<K,V> first = entryForHash(this, hash); HashEntry<K,V> e = first; int retries = -1; // 查找”key-value键值对“在”HashEntry链表上对应的节点“; // 无论找没找到,最后都会不断的自旋;在自旋期间,若通过tryLock()获取锁成功则返回;否则自旋MAX_SCAN_RETRIES次数之后,强制获取”锁“并退出。 // 若在自旋期间,HashEntry链表的表头发生变化;则重新进行查找和自旋! while (!tryLock()) { HashEntry<K,V> f; if (retries < 0) { // 如果“遍历完该HashEntry链表,仍然没找到”要删除的键值对“对应的节点” // 或者“在该HashEntry链表上找到”要删除的键值对“对应的节点”,则设置retries=0 // 否则,设置e为下一个HashEntry节点。 if (e == null || key.equals(e.key)) retries = 0; else e = e.next; } // 自旋超过限制次数之后,获取锁并退出。 else if (++retries > MAX_SCAN_RETRIES) { lock(); break; } // 当“尝试了偶数次”时,就获取“当前Segment的第一个HashEntry”,即f。 // 然后,通过f!=first来判断“当前Segment的第一个HashEntry是否发生了改变”。 // 若是的话,则重置e,first和retries的值,并重新遍历。 else if ((retries & 1) == 0 && (f = entryForHash(this, hash)) != first) { e = first = f; retries = -1; } } }
说明:scanAndLock()的目标是获取锁。它的实现与scanAndLockForPut()类似,这里就不再过多说明。
总结:ConcurrentHashMap是线程安全的哈希表,它是通过“锁分段”来实现的。ConcurrentHashMap中包括了“Segment(锁分段)数组”,每个Segment就是一个哈希表,而且也是可重入的互斥锁。第一,Segment是哈希表表现在,Segment包含了“HashEntry数组”,而“HashEntry数组”中的每一个HashEntry元素是一个单向链表。即Segment是通过链式哈希表。第二,Segment是可重入的互斥锁表现在,Segment继承于ReentrantLock,而ReentrantLock就是可重入的互斥锁。
对于ConcurrentHashMap的添加,删除操作,在操作开始前,线程都会获取Segment的互斥锁;操作完毕之后,才会释放。而对于读取操作,它是通过volatile去实现的,HashEntry数组是volatile类型的,而volatile能保证“即对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入”,即我们总能读到其它线程写入HashEntry之后的值。
以上这些方式,就是ConcurrentHashMap线程安全的实现原理。
ConcurrentHashMap示例
下面,我们通过一个例子去对比HashMap和ConcurrentHashMap。
1 import java.util.*; 2 import java.util.concurrent.*; 3 4 /* 5 * ConcurrentHashMap是“线程安全”的哈希表,而HashMap是非线程安全的。 6 * 7 * 下面是“多个线程同时操作并且遍历map”的示例 8 * (01) 当map是ConcurrentHashMap对象时,程序能正常运行。 9 * (02) 当map是HashMap对象时,程序会产生ConcurrentModificationException异常。 10 * 11 * @author skywang 12 */ 13 public class ConcurrentHashMapDemo1 { 14 15 // TODO: map是HashMap对象时,程序会出错。 16 //private static Map<String, String> map = new HashMap<String, String>(); 17 private static Map<String, String> map = new ConcurrentHashMap<String, String>(); 18 public static void main(String[] args) { 19 20 // 同时启动两个线程对map进行操作! 21 new MyThread("ta").start(); 22 new MyThread("tb").start(); 23 } 24 25 private static void printAll() { 26 String key, value; 27 Iterator iter = map.entrySet().iterator(); 28 while(iter.hasNext()) { 29 Map.Entry entry = (Map.Entry)iter.next(); 30 key = (String)entry.getKey(); 31 value = (String)entry.getValue(); 32 System.out.print(key+" - "+value+", "); 33 } 34 System.out.println(); 35 } 36 37 private static class MyThread extends Thread { 38 MyThread(String name) { 39 super(name); 40 } 41 @Override 42 public void run() { 43 int i = 0; 44 while (i++ < 6) { 45 // “线程名” + "-" + "序号" 46 String val = Thread.currentThread().getName()+i; 47 map.put(String.valueOf(i), val); 48 // 通过“Iterator”遍历map。 49 printAll(); 50 } 51 } 52 } 53 }
(某一次)运行结果:
1 - tb1, 1 - tb1, 1 - tb1, 1 - tb1, 2 - tb2, 2 - tb2, 1 - tb1, 3 - ta3, 1 - tb1, 2 - tb2, 3 - tb3, 1 - tb1, 2 - tb2, 3 - tb3, 1 - tb1, 4 - tb4, 3 - tb3, 2 - tb2, 4 - tb4, 1 - tb1, 2 - tb2, 5 - ta5, 1 - tb1, 3 - tb3, 5 - tb5, 4 - tb4, 3 - tb3, 2 - tb2, 4 - tb4, 1 - tb1, 2 - tb2, 5 - tb5, 1 - tb1, 6 - tb6, 5 - tb5, 3 - tb3, 6 - tb6, 4 - tb4, 3 - tb3, 2 - tb2, 4 - tb4, 2 - tb2,
结果说明:如果将源码中的map改成HashMap对象时,程序会产生ConcurrentModificationException异常。
更多内容
2. Java多线程系列--“JUC集合”02之 CopyOnWriteArrayList
3. Java多线程系列--“JUC集合”03之 CopyOnWriteArraySet