飞道的博客

Java8 ConcurrentSkipListMap与ConcurrentSkipListSet (一) 源码解析

403人阅读  评论(0)

     目录

一、ConcurrentSkipListSet

二、ConcurrentSkipListMap

1、定义

2、Node / Index / HeadIndex

3、SkipList 跳跃表

4、构造方法

5、put / putIfAbsent 

6、remove / replace / replaceAll / clear

7、get / getOrDefault


      ConcurrentSkipListSet是基于ConcurrentSkipListMap实现的线程安全的NavigableSet接口实现类,与之对应的线程不安全的实现类就是基于TreeMap实现的TreeSet;ConcurrentNavigableMap是基于SkipList跳跃表实现的线程安全的NavigableMap实现类,后者对应的线程不安全的实现类就是基于红黑树实现的TreeMap。本篇博客就详细探讨这两类的实现细节。

一、ConcurrentSkipListSet

      ConcurrentSkipListSet的类继承关系如下:

       

     其实现的核心接口NavigableSet的另一个典型实现类就是TreeSet,跟TreeSet基于TreeMap实现一样, ConcurrentSkipListSet的实现基于ConcurrentSkipListMap的,添加和删除元素,元素遍历等都是基于ConcurrentSkipListMap方法的,value固定为Boolean.TRUE,其常用方法实现如下:


  
  1. public ConcurrentSkipListSet() {
  2. m = new ConcurrentSkipListMap<E,Object>();
  3. }
  4. public ConcurrentSkipListSet(Comparator<? super E> comparator) {
  5. m = new ConcurrentSkipListMap<E,Object>(comparator);
  6. }
  7. public boolean add(E e) {
  8. return m.putIfAbsent(e, Boolean.TRUE) == null;
  9. }
  10. public boolean remove(Object o) {
  11. return m.remove(o, Boolean.TRUE);
  12. }
  13. public Iterator<E> iterator() {
  14. return m.navigableKeySet().iterator();
  15. }
  16. public Iterator<E> descendingIterator() {
  17. return m.descendingKeySet().iterator();
  18. }

二、ConcurrentSkipListMap

1、定义

     ConcurrentSkipListMap的类继承关系如下:

     

     从上图可知,ConcurrentNavigableMap继承自ConcurrentMap和NavigableMap,后者的典型实现类就是TreeMap,纯粹是基于红黑树实现的,可以参考《java8 TreeMap接口实现源码解析》。ConcurrentNavigableMap没有添加新的方法,只是改写了父类接口的方法定义,如下:

这些方法的用途可以参考API,会挑选其中典型的方法研究其实现细节。该类包含的属性如下:


  
  1. //头索引节点
  2. private transient volatile HeadIndex<K,V> head;
  3. //比较大小的比较器
  4. final Comparator<? super K> comparator;
  5. //key视图,下面这些视图都是在请求相关方法时才会创建
  6. private transient KeySet<K> keySet;
  7. //键值对视图
  8. private transient EntrySet<K,V> entrySet;
  9. //value视图
  10. private transient Values<V> values;
  11. //倒序遍历的视图
  12. private transient ConcurrentNavigableMap<K,V> descendingMap;

 包含的静态常量如下:


  
  1. //初始化head属性时使用,表示一个空节点
  2. private static final Object BASE_HEADER = new Object();
  3. private static final int EQ = 1;
  4. private static final int LT = 2;
  5. private static final int GT = 0; // Actually checked as !LT

包含的静态属性通过static代码块初始化,如下:

2、Node / Index / HeadIndex

     这三个都是内部类,其中Node表示一个键值对,其定义如下:


  
  1. static final class Node<K,V> {
  2. //键值对的key和value
  3. final K key;
  4. volatile Object value;
  5. //链表中的下一个节点
  6. volatile Node<K,V> next;
  7. Node(K key, Object value, Node<K,V> next) {
  8. this.key = key;
  9. this.value = value;
  10. this.next = next;
  11. }
  12. Node(Node<K,V> next) {
  13. this.key = null;
  14. this.value = this;
  15. this.next = next;
  16. }
  17. //cas修改value
  18. boolean casValue(Object cmp, Object val) {
  19. return UNSAFE.compareAndSwapObject( this, valueOffset, cmp, val);
  20. }
  21. //cas修改next
  22. boolean casNext(Node<K,V> cmp, Node<K,V> val) {
  23. return UNSAFE.compareAndSwapObject( this, nextOffset, cmp, val);
  24. }
  25. boolean isMarker() {
  26. return value == this;
  27. }
  28. boolean isBaseHeader() {
  29. return value == BASE_HEADER;
  30. }
  31. //插入一个value指向自己的节点,next为f的节点
  32. //删除某个节点时会将其value置为null,然后调用此方法在该节点后插入一个marker,将该节点从next链表中移除
  33. //marker在这里是打一个标识同时保存下一个节点的引用,保证将其从next链表中移除失败,即cas修改前一个节点的next属性失败的情形下该节点可以被正常移除
  34. boolean appendMarker(Node<K,V> f) {
  35. return casNext(f, new Node<K,V>(f));
  36. }
  37. //在查询或者修改节点时,发现某个节点的value已经是null了,会调用此方法
  38. //b是前一个节点,f是next节点
  39. void helpDelete(Node<K,V> b, Node<K,V> f) {
  40. if (f == next && this == b.next) {
  41. //再次检查链表关系
  42. if (f == null || f.value != f)
  43. //该节点未插入marker节点,此处重新插入,下次调用时进入else分支
  44. casNext(f, new Node<K,V>(f));
  45. else
  46. //f是一个marker节点
  47. //将this和f都从链表中移除
  48. b.casNext( this, f.next);
  49. }
  50. }
  51. V getValidValue() {
  52. Object v = value;
  53. if (v == this || v == BASE_HEADER) //value是无效
  54. return null;
  55. @SuppressWarnings("unchecked") V vv = (V)v;
  56. return vv;
  57. }
  58. AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
  59. Object v = value;
  60. if (v == null || v == this || v == BASE_HEADER) //value是无效
  61. return null;
  62. @SuppressWarnings("unchecked") V vv = (V)v;
  63. return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
  64. }
  65. //获取属性偏移量
  66. private static final sun.misc.Unsafe UNSAFE;
  67. private static final long valueOffset;
  68. private static final long nextOffset;
  69. static {
  70. try {
  71. UNSAFE = sun.misc.Unsafe.getUnsafe();
  72. Class<?> k = Node. class;
  73. valueOffset = UNSAFE.objectFieldOffset
  74. (k.getDeclaredField( "value"));
  75. nextOffset = UNSAFE.objectFieldOffset
  76. (k.getDeclaredField( "next"));
  77. } catch (Exception e) {
  78. throw new Error(e);
  79. }
  80. }
  81. }

   Index是HeadIndex的父类,都表示SkipList中的索引节点,其实现如下:


  
  1. static class Index<K,V> {
  2. //关联的node节点
  3. final Node<K,V> node;
  4. //down指向的节点的node和当前节点的node是同一个,但是down节点位于下一个层级,层级越往下包含的节点数越多,最底层包含了所有的node节点
  5. final Index<K,V> down;
  6. //right指向的节点的key大于当前节点,跟当前节点位于同一个层级,沿着right属性构成的链表遍历,node的key值越来越大
  7. volatile Index<K,V> right; ,
  8. Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
  9. this.node = node;
  10. this.down = down;
  11. this.right = right;
  12. }
  13. //cas修改right属性
  14. final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
  15. return UNSAFE.compareAndSwapObject( this, rightOffset, cmp, val);
  16. }
  17. final boolean indexesDeletedNode() {
  18. return node.value == null;
  19. }
  20. //将新节点插入到当前节点的rigth链表中,原right节点的前面
  21. final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
  22. Node<K,V> n = node;
  23. newSucc.right = succ;
  24. //node的vlue不为空时,将right由succ修改成newSucc
  25. return n.value != null && casRight(succ, newSucc);
  26. }
  27. //将succ从right链表中移除
  28. final boolean unlink(Index<K,V> succ) {
  29. //node的vlue不为空时,将right由succ修改成succ.right
  30. return node.value != null && casRight(succ, succ.right);
  31. }
  32. //获取right属性偏移量
  33. private static final sun.misc.Unsafe UNSAFE;
  34. private static final long rightOffset;
  35. static {
  36. try {
  37. UNSAFE = sun.misc.Unsafe.getUnsafe();
  38. Class<?> k = Index.class;
  39. rightOffset = UNSAFE.objectFieldOffset
  40. (k.getDeclaredField( "right"));
  41. } catch (Exception e) {
  42. throw new Error(e);
  43. }
  44. }
  45. }
  46. static final class HeadIndex<K,V> extends Index<K,V> {
  47. //level记录层级
  48. final int level;
  49. HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
  50. super(node, down, right);
  51. this.level = level;
  52. }
  53. }

3、SkipList 跳跃表

       跳跃表是一种改进自链表的数据结构,用于存储有序的数据,通过空间换时间的方法来提高数据检索的速度。 普通的链表或者数组插入和删除的效率都是O(1),但是查找的效率是O(n),如果是有序链表或者有序数组,则可以通过二分法快速查找,复杂度是O(logn)。跳跃表在理想情况下是O(logn),最差的情况下是O(n),因为跳跃表“跳跃”的下一个节点是完全随机的。其数据结构如下图:

     如图所示,跳跃表是有多个层级的,每个层级都是一个有序的链表,越往上的层级节点数越少,最底层则包含了所有的节点,每一层的节点的起始节点都是head节点。      

     如图所示,跳跃表的节点有两种,一种是最底层的,层级是0的节点,跟普通链表的节点一样,通过next属性构成链表,就是上面的Node类;一种是索引节点,就是上面的Index类,索引节点包含对Node的引用,除此之外还有两个属性,down属性保存下一层级节点的引用,即图中向下的箭头引用,right属性保存同一层级中比当前节点指向的Node大的的节点,即图中向右的箭头引用,注意通过down属性链接起来的节点其指向的Node都是同一个且这些节点都是在Node节点创建时根据Node节点的层级一起创建的。还有一种特殊的索引节点,图中的head节点,会额外保存当前跳跃表的层级,即HeadIndex类中的level属性,在插入新节点时会判断新节点的层级是否大于当前head节点的层级,如果是,需要相应的增加一个head节点,另外,元素遍历时也是通过左上角的head节点开始遍历的,先往右查找,如果碰到比自己大的节点,则在下一层级继续往右查找,直到找到最底层的节点,再通过next属性遍历,找到匹配的节点。跳跃的实现相比普通链表增加了索引节点,通过索引节点跳过一些中间节点,从而提高查找的效率,这就是所谓的空间换时间了。

     注意,跳跃表的层级是取决于所有节点的最大层级的,即如果某个节点的层级大于当前跳跃表的层级,会将当前跳跃表的层级加1,同时创建一个新的head节点,新head节点的right属性指向该节点。而新节点所属的层级是随机的,在插入时通过随机算法确定的,50%的概率是0,25%的概率是1,12.5%的概率是2,层级越高的概率越低。

    理解上述逻辑,可以通过如下测试用例Debug:


  
  1. @Test
  2. public void test() throws Exception {
  3. Random random= new Random();
  4. Map<Integer,Integer> test= new ConcurrentSkipListMap<>();
  5. for( int i= 0;i< 90;i++){
  6. test.put(random.nextInt( 90), 1);
  7. }
  8. test.put(random.nextInt( 100), 1);
  9. test.put(random.nextInt( 100), 1);
  10. test.put(random.nextInt( 100), 1);
  11. test.put(random.nextInt( 100), 1);
  12. for( int key:test.keySet()){
  13. System.out.println(key);
  14. }
  15. }

 在后面的test.put处打断点,然后进入到doPut方法调试,前面插入的90个节点保证跳跃表已经形成了多个层级; 如果想要查看层级非0的节点的插入逻辑,则直接在doPut方法内已经算出随机数的地方打断点。下图是进入doPut方法后,打印的head节点:

从图中可知,通过down属性链接的多个节点指向同一个Node节点。 

4、构造方法


  
  1. public ConcurrentSkipListMap() {
  2. this.comparator = null;
  3. initialize();
  4. }
  5. public ConcurrentSkipListMap(Comparator<? super K> comparator) {
  6. this.comparator = comparator;
  7. initialize();
  8. }
  9. public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
  10. this.comparator = null;
  11. initialize();
  12. //使用父类AbstractMap的实现,遍历m中的键值对,通过put方法添加到当前map中
  13. putAll(m);
  14. }
  15. public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
  16. this.comparator = m.comparator();
  17. initialize();
  18. buildFromSorted(m);
  19. }
  20. private void initialize() {
  21. keySet = null;
  22. entrySet = null;
  23. values = null;
  24. descendingMap = null;
  25. head = new HeadIndex<K,V>( new Node<K,V>( null, BASE_HEADER, null),
  26. null, null, 1);
  27. }
  28. //该方法是在构造方法中调用的,不会被并行访问,所以构建跳跃表的过程中没有使用CAS
  29. private void buildFromSorted(SortedMap<K, ? extends V> map) {
  30. if (map == null)
  31. throw new NullPointerException();
  32. HeadIndex<K,V> h = head;
  33. Node<K,V> basepred = h.node;
  34. ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();
  35. //初始化数组元素的
  36. for ( int i = 0; i <= h.level; ++i)
  37. preds. add( null);
  38. Index<K,V> q = h;
  39. //将每一层的head节点保存到preds中,下面的while循环过程中会把同一层级的最右侧的节点保存到preds中
  40. for ( int i = h.level; i > 0; --i) {
  41. preds. set(i, q);
  42. q = q.down;
  43. }
  44. Iterator<? extends Map.Entry<? extends K, ? extends V>> it =
  45. map.entrySet().iterator();
  46. //因为SortedMap中的节点是已经排序好了,所以这里直接构建跳跃表即可,不需要为新节点查找插入位置
  47. while (it.hasNext()) {
  48. Map.Entry<? extends K, ? extends V> e = it.next();
  49. //生成一个随机数
  50. int rnd = ThreadLocalRandom.current().nextInt();
  51. int j = 0;
  52. //计算新节点的层级
  53. if ((rnd & 0x80000001) == 0) { //如果rnd是一个偶数
  54. do {
  55. ++j;
  56. } while (((rnd >>>= 1) & 1) != 0); //将rnd右移1位,即除以2,如果结果是偶数就终止循环,否则继续
  57. //如果大于head的level
  58. if (j > h.level) j = h.level + 1;
  59. }
  60. K k = e.getKey();
  61. V v = e.getValue();
  62. if (k == null || v == null)
  63. throw new NullPointerException();
  64. Node<K,V> z = new Node<K,V>(k, v, null);
  65. basepred.next = z;
  66. basepred = z;
  67. if (j > 0) {
  68. Index<K,V> idx = null;
  69. //从层级1开始往上逐一处理
  70. for ( int i = 1; i <= j; ++i) {
  71. idx = new Index<K,V>(z, idx, null);
  72. if (i > h.level)
  73. //创建一个新的head节点
  74. h = new HeadIndex<K,V>(h.node, h, idx, i);
  75. if (i < preds.size()) {
  76. preds. get(i).right = idx; //index作为上一个节点的right节点
  77. preds. set(i, idx); //更新i,即当前节点是这一层级中的最右边的节点
  78. } else
  79. //i>h.level的情形
  80. preds. add(idx);
  81. }
  82. }
  83. }
  84. //更新head
  85. head = h;
  86. }

5、put / putIfAbsent 

        上述两个方法的实现都是doPut方法,该方法的实现整体上可以分为两步,第一步是从最高层级的head索引节点开始往右查找,如果碰到一个比自己大的节点,则进入下一个层级继续往右查找,直到该节点是最底层的索引节点了为止,再根据该索引节点指向的链表节点,通过next属性往后遍历找到一个合适的位置,当前节点大于前面的节点小于后面的节点,然后插入到next链表中。第二步是计算当前节点所属的层级,如果层级不是0,则构建每一层级对应的索引节点,各索引节点通过down属性链接起来且都指向新创建的链表节点,然后从高层级往下遍历,将各索引节点同同一层级的节点建立关联关系,这期间如果新节点的层级高于head节点的层级,需要将当前head节点的层级加1,并创建一个对应的head节点,新的head节点的right属性指向该新节点对应的同一层级的索引节点。


  
  1. public V put(K key, V value) {
  2. if ( value == null)
  3. throw new NullPointerException();
  4. return doPut(key, value, false);
  5. }
  6. public V putIfAbsent(K key, V value) {
  7. if ( value == null)
  8. throw new NullPointerException();
  9. return doPut(key, value, true);
  10. }
  11. private V doPut(K key, V value, boolean onlyIfAbsent) {
  12. Node<K,V> z; // added node
  13. if (key == null)
  14. throw new NullPointerException();
  15. Comparator<? super K> cmp = comparator;
  16. //在next链表中找到合适的位置,将新节点插入进去
  17. outer: for (;;) {
  18. //findPredecessor方法返回当前Map中小于key的最大的一个索引节点所指向的节点,且该索引节点位于最底层了,然后遍历通过next属性构成的链表
  19. //直到找到一个大于key的节点,即下面c小于0的情形,将新节点插入到该节点的前面
  20. for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
  21. if (n != null) {
  22. Object v; int c;
  23. Node<K,V> f = n.next;
  24. if (n != b.next) //next节点变了,终止内层for循环,通过外层的for循环重新获取b和n
  25. break;
  26. if ((v = n. value) == null) { //n被删除了
  27. n.helpDelete(b, f);
  28. break;
  29. }
  30. if (b. value == null || v == n) //b被删除了,下一次findPredecessor会将b从链表中移除
  31. break;
  32. if ((c = cpr(cmp, key, n.key)) > 0) { //如果key大于n.key,继续遍历通过next属性构成的链表中的下一个节点
  33. b = n;
  34. n = f;
  35. continue;
  36. }
  37. if (c == 0) {
  38. //如果找到key相等的节点,
  39. if (onlyIfAbsent || n.casValue(v, value)) {
  40. //onlyIfAbsent为true表示不需要修改,为false则修改value,返回原来的value
  41. @SuppressWarnings( "unchecked") V vv = (V)v;
  42. return vv;
  43. }
  44. break; //如果cas修改失败,则继续for循环尝试修改,直到修改成功为止
  45. }
  46. //c小于0会进入下面的new Node
  47. }
  48. //如果n为null或者c小于0,c小于0时,b.key < key <n.key
  49. //创建一个新节点,插入到b和n的中间
  50. z = new Node<K,V>(key, value, n);
  51. if (!b.casNext(n, z))
  52. break; //如果修改失败则重试
  53. break outer; //终止外层的for循环
  54. } //内层for循环
  55. } //外层for循环
  56. int rnd = ThreadLocalRandom.nextSecondarySeed();
  57. if ((rnd & 0x80000001) == 0) { //如果是偶数
  58. //level表示新节点的层级,从1开始增加,0表示最底层的一个层架
  59. int level = 1, max;
  60. while (((rnd >>>= 1) & 1) != 0) //将rnd除以2以后,如果是奇数level加1
  61. ++level;
  62. //idx表示当前节点通过down属性构成的一个链表,所有节点都指向新节点z
  63. Index<K,V> idx = null;
  64. HeadIndex<K,V> h = head;
  65. if (level <= (max = h.level)) { //如果level小于head的level,则创建一个通过down属性构成的Index链表
  66. for ( int i = 1; i <= level; ++i)
  67. //idx是作为新节点的down节点的
  68. idx = new Index<K,V>(z, idx, null);
  69. }
  70. else { //如果level大于head的level,需要增加head节点的层级
  71. level = max + 1; //比head大的话,只能在原来的基础上加1
  72. //创建一个Index数组
  73. @SuppressWarnings( "unchecked")Index<K,V>[] idxs =
  74. (Index<K,V>[]) new Index<?,?>[level+ 1]; //加1是为了加上底层0对应的Index
  75. //初始化数组元素
  76. for ( int i = 1; i <= level; ++i)
  77. idxs[i] = idx = new Index<K,V>(z, idx, null);
  78. for (;;) {
  79. h = head;
  80. int oldLevel = h.level;
  81. if (level <= oldLevel) //初始状态下level肯定大于oldLevel
  82. break;
  83. HeadIndex<K,V> newh = h;
  84. Node<K,V> oldbase = h.node;
  85. //创建新层级对应的head节点,down节点指向原来的head,right节点指向新节点z对应的索引节点
  86. //通常oldLevel就比level小1,即只创建一个HeadIndex节课
  87. for ( int j = oldLevel+ 1; j <= level; ++j)
  88. newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
  89. if (casHead(h, newh)) { //cas修改head,如果修改失败说明其他线程在修改head,则重新for循环读取head
  90. h = newh; //使用最新的head
  91. idx = idxs[level = oldLevel]; //此处level等于oldLevel
  92. break;
  93. }
  94. }
  95. }
  96. //如果新节点z对应的level不是0,则创建对应层级的down链表,如果层级大于head的还需要调整head的层级,创建新的head节点,新层级的head节点的right节点
  97. //就指向新节点z
  98. // find insertion points and splice in
  99. splice: for ( int insertionLevel = level;;) {
  100. int j = h.level;
  101. for (Index<K,V> q = h, r = q.right, t = idx;;) {
  102. if (q == null || t == null)
  103. break splice;
  104. if (r != null) {
  105. Node<K,V> n = r.node;
  106. // compare before deletion check avoids needing recheck
  107. int c = cpr(cmp, key, n.key);
  108. if (n. value == null) { //r节点是无效的
  109. if (!q.unlink(r)) //将r从q的right链表中移除,移除失败重新读取q
  110. break;
  111. r = q.right; //获取新的right节点
  112. continue;
  113. }
  114. if (c > 0) { //key大于n.key,则继续遍历下一个right
  115. q = r;
  116. r = r.right;
  117. continue;
  118. }
  119. }
  120. //如果c小于等于0,即key小于等于n.key,或者r等于null
  121. //初始状态j肯定是大于insertionLevel,后面没for循环一次,j减1,j就会等于insertionLevel
  122. if (j == insertionLevel) {
  123. if (!q.link(r, t)) //将t插入到q的right链表中,插入到r的前面
  124. break; //link失败,重新读取h
  125. //link成功
  126. if (t.node. value == null) { //正常不可能为null,极端并发下可能为null,因为此时已经将新节点插入到链表中了,有可能被其他某个线程给删除了
  127. findNode(key); //通过findNode将该节点移除,然后终止外层for循环
  128. break splice;
  129. }
  130. //如果相等了,j和insertionLevel都会减1,后面的for循环中两个都会相等,如果不等则只是j减1
  131. if (--insertionLevel == 0) //到底层节点了,终止外层for循环
  132. break splice;
  133. }
  134. if (--j >= insertionLevel //j肯定大于等于insertionLevel
  135. && j < level) //如果j大于level,说明未遍历到新节点所在的最高层级,小于level了说明上一个层级的right节点处理好了,需要遍历下一个层级
  136. t = t.down;
  137. //遍历下一个层级的节点
  138. q = q.down;
  139. r = q.right;
  140. } //内层for循环
  141. } //外层for循环
  142. } //if结束
  143. return null;
  144. }
  145. //从head节点开始遍历,找到小于key的最大的一个索引节点指向的节点
  146. private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
  147. if (key == null)
  148. throw new NullPointerException(); // don't postpone errors
  149. for (;;) {
  150. //从head开始往后遍历
  151. for (Index<K,V> q = head, r = q.right, d;;) {
  152. if (r != null) {
  153. Node<K,V> n = r.node;
  154. K k = n.key;
  155. if (n. value == null) { //r节点即将被删除
  156. //将r从q的right链表中移除,如果q的node的value为null或者cas失败都会则返回false
  157. if (!q.unlink(r))
  158. break; //终止内层for循环,通过外层的for循环重新开始,重新读取head
  159. r = q.right; //如果unlink成功,重新读取r
  160. continue;
  161. }
  162. //n.value不为null
  163. if (cpr(cmp, key, k) > 0) {
  164. //如果key大于k,继续遍历下一个right
  165. q = r;
  166. r = r.right;
  167. continue;
  168. }
  169. }
  170. //如果key小于k 或者r等于null
  171. //如果down节点为null,说明已经到最下面的一层节点了,如果不为null,则继续遍历下一层节点
  172. //最简单的一个场景,head只有一个层级,即down为null,right不为null,会沿着right属性构成的链表一直遍历直到遇到一个大于key的节点
  173. //此时q就是小于key的最大的一个节点
  174. //如果down不为null,则遍历down节点的right,注意down节点和q节点指向的node是同一个,同样的沿着right属性构成的链表一直遍历直到遇到
  175. //一个大于key的节点,q就是上一个小于key的最大的节点
  176. if ((d = q.down) == null)
  177. return q.node;
  178. //如果down不为null,则继续用down的right节点跟key比
  179. q = d;
  180. r = d.right;
  181. }
  182. }
  183. }
  184. //c不为空null,通过c比较大小,否则强转成Comparable比较
  185. static final int cpr(Comparator c, Object x, Object y) {
  186. return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y);
  187. }
  188. private Node<K,V> findNode(Object key) {
  189. if (key == null)
  190. throw new NullPointerException(); // don't postpone errors
  191. Comparator<? super K> cmp = comparator;
  192. outer: for (;;) {
  193. for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
  194. Object v; int c;
  195. if (n == null) //没有匹配的节点,终止外层循环,返回null
  196. break outer;
  197. Node<K,V> f = n.next;
  198. if (n != b.next) //重新读取b
  199. break;
  200. if ((v = n. value) == null) { //n被删除了
  201. n.helpDelete(b, f);
  202. break;
  203. }
  204. if (b. value == null || v == n) //b呗删除了
  205. break;
  206. if ((c = cpr(cmp, key, n.key)) == 0) //找到目标节点
  207. return n;
  208. if (c < 0) //没有匹配节点,终止外层循环,返回null
  209. break outer;
  210. //c大于0,继续遍历下一个节点
  211. b = n;
  212. n = f;
  213. }
  214. }
  215. return null;
  216. }
  217. private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
  218. return UNSAFE.compareAndSwapObject( this, headOffset, cmp, val);
  219. }

6、remove / replace / replaceAll / clear

        remove方法的核心实现是doRemove,先按照同样的方式遍历找到目标节点,然后将该节点的value置为null,紧接着在该节点后插入一个空的marker节点,如果成功了则将该节点和之前插入对的marker节点一起从链表中移除,插入marker节点的原因就是为了保证后面的从链表移除的动作可以顺利完成,最后再移除该节点对应的索引节点,将其从同一层级的前一个节点的right链表中移除。 最后两步的移除动作,也可以并行的被其他线程执行,他们判断所引用的Node节点的value为null,会自动执行对应的删除动作,所以删除动作都是CAS实现的。


  
  1. public V remove(Object key) {
  2. return doRemove(key, null);
  3. }
  4. public boolean remove(Object key, Object value) {
  5. if (key == null)
  6. throw new NullPointerException();
  7. return value != null && doRemove(key, value) != null;
  8. }
  9. //将等于key的节点的value替换成指定值,返回替换前的值,相当于put方法
  10. public V replace(K key, V value) {
  11. if (key == null || value == null)
  12. throw new NullPointerException();
  13. for (;;) {
  14. Node<K,V> n; Object v;
  15. if ((n = findNode(key)) == null)
  16. return null; //如果没有找到目标节点
  17. //校验n.value不为空是为了避免该节点被删除了
  18. //然后cas修改value
  19. if ((v = n. value) != null && n.casValue(v, value)) {
  20. @SuppressWarnings( "unchecked") V vv = (V)v;
  21. return vv; //返回以前的值
  22. }
  23. }
  24. }
  25. //要求key和value都相等才替换,返回替换是否成功
  26. public boolean replace(K key, V oldValue, V newValue) {
  27. if (key == null || oldValue == null || newValue == null)
  28. throw new NullPointerException();
  29. for (;;) {
  30. Node<K,V> n; Object v;
  31. if ((n = findNode(key)) == null)
  32. return false;
  33. if ((v = n. value) != null) {
  34. if (!oldValue. equals(v)) //如果当前值不是oldValue返回false
  35. return false;
  36. if (n.casValue(v, newValue)) //如果cas修改成功,返回true
  37. return true;
  38. }
  39. }
  40. }
  41. public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
  42. if (function == null) throw new NullPointerException();
  43. V v;
  44. //获取第一个有效节点后,通过next遍历所有的节点
  45. for (Node<K,V> n = findFirst(); n != null; n = n.next) {
  46. while ((v = n.getValidValue()) != null) { //获取有效value
  47. V r = function.apply(n.key, v);
  48. //返回值为空,抛出异常
  49. if (r == null) throw new NullPointerException();
  50. if (n.casValue(v, r)) //cas修改value成功则终止内层while循环,处理下一个节点
  51. break;
  52. }
  53. }
  54. }
  55. //获取第一个有效节点,其实就是head.node.next
  56. final Node<K,V> findFirst() {
  57. for (Node<K,V> b, n;;) {
  58. if ((n = (b = head.node).next) == null)
  59. return null; //如果next为null,返回null
  60. if (n. value != null)
  61. return n;
  62. //如果n.value为null,则删除该节点
  63. n.helpDelete(b, n.next);
  64. }
  65. }
  66. final V doRemove(Object key, Object value) {
  67. if (key == null)
  68. throw new NullPointerException();
  69. Comparator<? super K> cmp = comparator;
  70. outer: for (;;) {
  71. for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
  72. Object v; int c;
  73. if (n == null) //没有匹配的节点,终止外层for循环,返回null
  74. break outer;
  75. Node<K,V> f = n.next;
  76. if (n != b.next) //next节点被修改了,重新读取b
  77. break;
  78. if ((v = n. value) == null) { //n被删除了
  79. n.helpDelete(b, f); //删除节点n
  80. break;
  81. }
  82. if (b. value == null || v == n) // b被删除了,重新读取b
  83. break;
  84. if ((c = cpr(cmp, key, n.key)) < 0) //key小于n.key,不会有匹配的节点了
  85. break outer;
  86. if (c > 0) { //key大于n.key,继续遍历下一个next节点
  87. b = n;
  88. n = f;
  89. continue;
  90. }
  91. //c等于0
  92. if ( value != null && ! value. equals(v)) //value不为空跟当前节点的value不一致,终止外层for循环,返回null
  93. break outer;
  94. if (!n.casValue(v, null)) //cas修改失败,内层for循环重试
  95. break;
  96. //casValue成功,在n和f之间插入一个空节点,如果成功则将b的next节点修改成f
  97. if (!n.appendMarker(f) || !b.casNext(n, f))
  98. //value已经置为null,其他线程不可能修改n的next属性,此时只有修改b的next属性会失败
  99. findNode(key); //上述操作有一个失败则执行findNode,借此清除节点和关联的索引节点
  100. else {
  101. //修改成功,通过findPredecessor清理关联的Index节点
  102. findPredecessor(key, cmp); // clean index
  103. if (head.right == null) //如果head的right节点为null,则尝试减少层级
  104. tryReduceLevel();
  105. }
  106. @SuppressWarnings( "unchecked") V vv = (V)v;
  107. return vv;
  108. }
  109. }
  110. return null;
  111. }
  112. public void clear() {
  113. for (;;) {
  114. Node<K,V> b, n;
  115. HeadIndex<K,V> h = head, d = (HeadIndex<K,V>)h.down;
  116. if (d != null)
  117. casHead(h, d); //修改head,原head的right链表就都被移除了
  118. //d为null,遍历到最底层的节点了
  119. else if ((b = h.node) != null && (n = b.next) != null) {
  120. Node<K,V> f = n.next; // remove values
  121. if (n == b.next) { //b未改变
  122. Object v = n. value;
  123. if (v == null) //n被删除了
  124. n.helpDelete(b, f);
  125. //v不为null,将n的value置为null并插入marker,将b的next由n修改成f
  126. else if (n.casValue(v, null) && n.appendMarker(f))
  127. b.casNext(n, f); //如果cas成功,则n从链表中移除了,如果失败了,假如此时其他线程在并行的往b后面插入一个节点,则下次遍历到n时通过helpDelete将其从链表中移除
  128. }
  129. }
  130. else
  131. break;
  132. }
  133. }
  134. private void tryReduceLevel() {
  135. HeadIndex<K,V> h = head;
  136. HeadIndex<K,V> d;
  137. HeadIndex<K,V> e;
  138. if (h.level > 3 &&
  139. (d = (HeadIndex<K,V>)h.down) != null &&
  140. (e = (HeadIndex<K,V>)d.down) != null &&
  141. e.right == null &&
  142. d.right == null &&
  143. h.right == null && //从head往下连续三个层级的right都为null
  144. casHead(h, d) && //将head从h修改成d
  145. h.right != null) // 最后一次检查,如果不为null则恢复
  146. casHead(d, h); // try to backout
  147. }

7、get / getOrDefault

      其底层实现doGet方法的实现和findNode方法是基本一致的,如下:


  
  1. public V get(Object key) {
  2. return doGet(key);
  3. }
  4. public V getOrDefault(Object key, V defaultValue) {
  5. V v;
  6. return (v = doGet(key)) == null ? defaultValue : v;
  7. }
  8. //doGet方法的实现和findNode方法是基本一致的
  9. private V doGet(Object key) {
  10. if (key == null)
  11. throw new NullPointerException();
  12. Comparator<? super K> cmp = comparator;
  13. outer: for (;;) {
  14. for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
  15. Object v; int c;
  16. if (n == null) //没有匹配节点
  17. break outer;
  18. Node<K,V> f = n.next;
  19. if (n != b.next) //重新读取b
  20. break;
  21. if ((v = n.value) == null) { //n被删除了
  22. n.helpDelete(b, f);
  23. break;
  24. }
  25. if (b.value == null || v == n) // b被删除了
  26. break;
  27. if ((c = cpr(cmp, key, n.key)) == 0) { //找到目标节点
  28. @SuppressWarnings("unchecked") V vv = (V)v;
  29. return vv;
  30. }
  31. if (c < 0)
  32. break outer; 没有匹配节点
  33. //c大于0,则遍历下一个next节点
  34. b = n;
  35. n = f;
  36. }
  37. }
  38. return null;
  39. }

 

 


转载:https://blog.csdn.net/qq_31865983/article/details/105586590
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场