1.7中的ConcurrentHashMap整体上可以看作是一个双层数组的结构。外层是Segment数组,每个Segment内是一个HashEntry数组,类似于一个HashMap,数组的每个Segment在操作时都相对独立。Segment在初始化后不允许扩容,HashEntry允许扩容。在加锁的过程中只会对某个Segment加锁而不是整个数组。

构造方法

  1. //无参构造器会用默认参数调用该构造器
  2. //默认参数为initialCapacity=16,loadFactor=0.75f,concurrencyLevel=16
  3. public ConcurrentHashMap(int initialCapacity,
  4. float loadFactor, int concurrencyLevel) {
  5. if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
  6. throw new IllegalArgumentException();
  7. if (concurrencyLevel > MAX_SEGMENTS)
  8. concurrencyLevel = MAX_SEGMENTS;
  9. // Find power-of-two sizes best matching arguments
  10. int sshift = 0;
  11. //Segment数组的长度
  12. int ssize = 1;
  13. //Segment数组的长度要为2的n次幂且大于等于concurrencyLevel
  14. while (ssize < concurrencyLevel) {
  15. ++sshift;
  16. ssize <<= 1;
  17. }
  18. this.segmentShift = 32 - sshift;
  19. this.segmentMask = ssize - 1;
  20. if (initialCapacity > MAXIMUM_CAPACITY)
  21. initialCapacity = MAXIMUM_CAPACITY;
  22. //c用来计算Segment内数组的大小
  23. int c = initialCapacity / ssize;
  24. if (c * ssize < initialCapacity)
  25. ++c;
  26. //cap为真正的Segment内的数组大小 需要调整为2的n次幂
  27. int cap = MIN_SEGMENT_TABLE_CAPACITY;
  28. while (cap < c)
  29. cap <<= 1;
  30. //创建0位置的Segment
  31. Segment<K,V> s0 =
  32. new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
  33. (HashEntry<K,V>[])new HashEntry[cap]);
  34. //创建Segment数组
  35. Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
  36. //将s0赋值给Segment[0]
  37. UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
  38. this.segments = ss;
  39. }

Put方法

  1. public V put(K key, V value) {
  2. Segment<K,V> s;
  3. if (value == null)//不允许空值
  4. throw new NullPointerException();
  5. int hash = hash(key);
  6. //j是找到的Segment数组索引
  7. int j = (hash >>> segmentShift) & segmentMask;
  8. //判断插入的位置是否为null,为null则创建一个Segment
  9. if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
  10. (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
  11. s = ensureSegment(j);
  12. return s.put(key, hash, value, false);
  13. }
  14. private Segment<K,V> ensureSegment(int k) {
  15. final Segment<K,V>[] ss = this.segments;
  16. long u = (k << SSHIFT) + SBASE; // raw offset
  17. Segment<K,V> seg;
  18. //为保证在当前位置只能有一个Segment被创建,这里用UNSAFE,
  19. if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {
  20. Segment<K,V> proto = ss[0]; // use segment 0 as prototype
  21. //从Segment[0]获取参数 HashEntry数组的大小和加载因子
  22. int cap = proto.table.length;
  23. float lf = proto.loadFactor;
  24. //计算扩容阈值
  25. int threshold = (int)(cap * lf);
  26. HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap];
  27. //再次检测当前位置是否已经有其他线程创造的Segment
  28. if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
  29. == null) { // recheck
  30. Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);
  31. //这里用cas保证写入成功
  32. while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
  33. == null) {
  34. if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))
  35. break;
  36. }
  37. }
  38. }
  39. return seg;
  40. }
  41. final V put(K key, int hash, V value, boolean onlyIfAbsent) {
  42. //tryLock 尝试获取锁,即使获取失败,后面的代码也会继续执行
  43. //Lock 获取锁,若获取失败,则进入阻塞状态
  44. //此时若尝试获取锁失败,则会引入自旋锁,在scanAndLockForPut实现
  45. HashEntry<K,V> node = tryLock() ? null :
  46. scanAndLockForPut(key, hash, value);
  47. V oldValue;
  48. //是HashEntry链表插入,和hashMap差不多
  49. try {
  50. HashEntry<K,V>[] tab = table;
  51. int index = (tab.length - 1) & hash;
  52. HashEntry<K,V> first = entryAt(tab, index);
  53. for (HashEntry<K,V> e = first;;) {
  54. if (e != null) {
  55. K k;
  56. if ((k = e.key) == key ||
  57. (e.hash == hash && key.equals(k))) {
  58. oldValue = e.value;
  59. if (!onlyIfAbsent) {
  60. e.value = value;
  61. ++modCount;
  62. }
  63. break;
  64. }
  65. e = e.next;
  66. }
  67. else {
  68. if (node != null)
  69. node.setNext(first);
  70. else
  71. node = new HashEntry<K,V>(hash, key, value, first);
  72. int c = count + 1;
  73. if (c > threshold && tab.length < MAXIMUM_CAPACITY)
  74. rehash(node);
  75. else
  76. setEntryAt(tab, index, node);
  77. ++modCount;
  78. count = c;
  79. oldValue = null;
  80. break;
  81. }
  82. }
  83. } finally {
  84. unlock();
  85. }
  86. return oldValue;
  87. }
  88. private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
  89. //获取要插入的Segment的第一个节点
  90. HashEntry<K,V> first = entryForHash(this, hash);
  91. HashEntry<K,V> e = first;
  92. HashEntry<K,V> node = null;
  93. //while循环为一直尝试获取锁 使用tryLock可以在未获取锁的时候进行
  94. //一些其它操作提高效率
  95. //retries变量起到引导作用 判断当前应该进行什么操作
  96. int retries = -1; // negative while locating node
  97. while (!tryLock()) {
  98. HashEntry<K,V> f; // to recheck first below
  99. //若retries小于0 则进行遍历链表操作
  100. if (retries < 0) {
  101. if (e == null) {//若遍历到表尾则创建HashEntry
  102. if (node == null) // speculatively create node
  103. node = new HashEntry<K,V>(hash, key, value, null);
  104. retries = 0;
  105. }
  106. else if (key.equals(e.key))//找到key相同的HashEntry 将retries置为0
  107. retries = 0;
  108. else
  109. e = e.next;
  110. }
  111. //此时为遍历链表的操作已经结束还未获取到锁 不能一直处于循环状态占用CPU内存
  112. //每进行一次循环会让retries变量+1 当retries大于64时 调用Lock方法阻塞
  113. else if (++retries > MAX_SCAN_RETRIES) {
  114. lock();
  115. break;
  116. }
  117. ////判断在循环等待的过程中 链表结构是否发生了变化即头结点发生改变
  118. //若改变则将retries置为-1 重新进行遍历链表操作
  119. //为了避免在判断头结点是否改变的过程中 释放的锁被其它线程抢走 只在retries为奇数时判断
  120. else if ((retries & 1) == 0 &&
  121. (f = entryForHash(this, hash)) != first) {
  122. e = first = f; // re-traverse if entry changed
  123. retries = -1;
  124. }
  125. }
  126. return node;
  127. }