ConcurrentSkipListMap源码分析

Posted wei_zw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConcurrentSkipListMap源码分析相关的知识,希望对你有一定的参考价值。

 

看一下跳跃表的示意图,途中蓝色的为头节点,头节点指向的是普通索引节点

 通过上图可以看到跳跃表的基本结构,下面分析一下普通索引节点和头节点的源码,可以发现头节点和普通索引节点的区别就是头节点有level的概念,而普通索引节点没有

static class Index<K,V> {
         //索引持有的数据节点
        final Node<K,V> node;
        //下一层索引
        final Index<K,V> down;
        //右侧索引
        volatile Index<K,V> right;

        //对指定数据节点,下一层索引以及右侧索引创建一个新索引
        Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
            this.node = node;
            this.down = down;
            this.right = right;
        }

        //CAS更新右侧索引
        final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
            return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val);
        }

        //判断当前节点是否是删除节点
        final boolean indexesDeletedNode() {
            return node.value == null;
        }

        //将newSucc节点插入到当前节点与succ节点之间
        final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
            Node<K,V> n = node;
            //新索引节点的右侧索引指向succ
            newSucc.right = succ;
            //如果当前节点不是删除节点,则cas更新右侧索引指向newSucc
            return n.value != null && casRight(succ, newSucc);
        }

        //删除succ
        final boolean unlink(Index<K,V> succ) {
            return node.value != null && casRight(succ, succ.right);
        }

       
        private static final sun.misc.Unsafe UNSAFE;
        private static final long rightOffset;
        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = Index.class;
                rightOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("right"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }

    //头节点,只有头节点才有层级概念
    static final class HeadIndex<K,V> extends Index<K,V> {
        final int level;
        HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
            super(node, down, right);
            this.level = level;
        }
    }

  

 

 

 public V put(K key, V value) {
         //如果value为null则抛出空指针异常
        if (value == null)
            throw new NullPointerException();
        //添加数据
        return doPut(key, value, false);
    }

   
    private V doPut(K key, V value, boolean onlyIfAbsent) {
        //声明一个需要添加到节点
        Node<K,V> z; 
        //如果key为null则抛出一个NPE           
        if (key == null)
            throw new NullPointerException();

        Comparator<? super K> cmp = comparator;
        outer: for (;;) {
              //对当前key找到前驱
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
                 //如果b的后继不为null
                if (n != null) {
                    Object v; int c;
                    //n的后继节点
                    Node<K,V> f = n.next;
                    //再次判断n是不是b的后继,如果不是则跳出循环(一致性读)
                    if (n != b.next)              
                        break;
                     //如果n节点已经被删除则跳出循环   
                    if ((v = n.value) == null) {   
                        n.helpDelete(b, f);
                        break;
                    }
                    //如果b节点被删除则跳出循环
                    if (b.value == null || v == n) 
                        break;
                     //如果key大于n节点的key则继续向后找   
                    if ((c = cpr(cmp, key, n.key)) > 0) {
                        b = n;
                        n = f;
                        continue;
                    }
                    //如果找到了key相等的节点
                    if (c == 0) {
                        if (onlyIfAbsent || n.casValue(v, value)) {
                            @SuppressWarnings("unchecked") V vv = (V)v;
                            return vv;
                        }
                        break; //CAS竞争失败则继续
                    }
                    // else c < 0; fall through
                }
                //如果后继节点为null则可以直接添加
                z = new Node<K,V>(key, value, n);
                //CAS设置失败则跳出内层循环,继续执行
                if (!b.casNext(n, z))
                    break;
                //CAS成功则跳出外层循环             
                break outer;
            }
        }
        
        int rnd = ThreadLocalRandom.nextSecondarySeed();
        //与10000000000000000000000000000001按位与的结果为0,表示最高位和最低位不为1
        if ((rnd & 0x80000001) == 0) { 

            int level = 1, max;
            //随机数从左到右连续有几个1,则level自增几次
            while (((rnd >>>= 1) & 1) != 0)
                ++level;
            Index<K,V> idx = null;
            HeadIndex<K,V> h = head;
            //如果level小于等于head节点的层级,则逐层创建索引且down之前idx
            if (level <= (max = h.level)) {
                for (int i = 1; i <= level; ++i)
                    idx = new Index<K,V>(z, idx, null);
            }
            else { //如果level大于head的层级
                //将level设置为max+1,即head的层级+1
                level = max + 1; 
               //创建新的索引数组 
               Index<K,V>[] idxs =
                    (Index<K,V>[])new Index<?,?>[level+1];
                //对索引数组进行赋值,0不使用,并将down指向前一个节点
                for (int i = 1; i <= level; ++i)
                    idxs[i] = idx = new Index<K,V>(z, idx, null);

                for (;;) {
                    h = head;
                    int oldLevel = h.level;
                    if (level <= oldLevel) //如果当前level小于oldLevel则跳出
                        break;
                    HeadIndex<K,V> newh = h;
                    Node<K,V> oldbase = h.node;
                    //对新增的层级生产头节点
                    for (int j = oldLevel+1; j <= level; ++j)
                        newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
                     //cas赋值头节点   
                    if (casHead(h, newh)) {
                       //将最新头节点赋值给h
                        h = newh;
                        //idx为之前的头节点
                        idx = idxs[level = oldLevel];
                        break;
                    }
                }
            }
            // 找到插入点,并插入数据
            splice: for (int insertionLevel = level;;) {
              //level为之前头节点的层级,j为新头节点的层级
                int j = h.level;
                for (Index<K,V> q = h, r = q.right, t = idx;;) {
                        //如果新头节点,或者老头节点为null则跳出外层循环
                    if (q == null || t == null)
                        break splice;
                      //r为新头节点的右节点,如果不为null  
                    if (r != null) {
                        //r索引持有的节点
                        Node<K,V> n = r.node;
                        //当前key和r索引持有的key进行比较
                        int c = cpr(cmp, key, n.key);
                        //如果n的值为null,则删除该节点,如果删除失败则重新循环
                        if (n.value == null) {
                            if (!q.unlink(r))
                                break;
                            r = q.right;
                            continue;
                        }
                        //如果key>n.key 则继续向后查找
                        if (c > 0) {
                            q = r;
                            r = r.right;
                            continue;
                        }
                    }
                    // 表示r为null,即找到该层的最后
                    
                    //如果是需要插入的层级
                    if (j == insertionLevel) {
                        //将t插入到q,r之间失败则重新开始
                        if (!q.link(r, t))
                            break; 
                         //插入成功,如果t的值为null,则需要删除   
                        if (t.node.value == null) {
                            findNode(key);
                            break splice;
                        }
                        //如果到最低层则跳出外层循环
                        if (--insertionLevel == 0)
                            break splice;
                    }
                    //如果 --j 大于等于insertLevel则继续处理下一层
                    if (--j >= insertionLevel && j < level)
                        t = t.down;
                    q = q.down;
                    r = q.right;
                }
            }
        }
        return null;
    }

  

    private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
        //如果key为null则抛出NPE
        if (key == null)
            throw new NullPointerException(); // don\'t postpone errors
        for (;;) {
            // q初始化为头索引节点,r为索引节点右侧索引节点
            for (Index<K,V> q = head, r = q.right, d;;) {
                //如果r不为nul
                if (r != null) {
                    //r索引节点持有的数据节点
                    Node<K,V> n = r.node;
                    //该数据节点的key
                    K k = n.key;
                    //如果n的值为nul则表示该索引节点需要删除
                    if (n.value == null) {
                        //删除索引节点,删除失败则重新开始
                        if (!q.unlink(r))
                            break;   
                         //删除成功后r为q的新右侧索引节点           
                        r = q.right;         
                        continue;
                    }
                    //r节点不是删除节点则比较key大小,r索引持有的key小于新插入的key则继续向右移
                    if (cpr(cmp, key, k) > 0) {
                        q = r;
                        r = r.right;
                        continue;
                    }
                }
                //表示r为null或者r节点持有的数据key大于新插入数据的key

                //如果q下一层为null则直接返回q持有的数据节点
                if ((d = q.down) == null)
                    return q.node;
                //如果q.down不为null,则向下一层查找    
                q = d;
                r = d.right;
            }
        }
    }

  

  
public V get(Object key) {
        return doGet(key);
    }

  private V doGet(Object key) {
        if (key == null)
            throw new NullPointerException();
        Comparator<? super K> cmp = comparator;
        outer: for (;;) {
            //b根据key找到的前驱节点
            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
                Object v; int c;
                //如果b的后继为null则跳出外层循环返回null
                if (n == null)
                    break outer;
                //f为n的后继节点    
                Node<K,V> f = n.next;
                //如果n不是b的后继则跳出循环重新开始
                if (n != b.next)                
                    break;
                 //如果n节点是需要删除节点则删除重新开始   
                if ((v = n.value) == null) {   
                    n.helpDelete(b, f);
                    break;
                }
                //如果b节点是删除节点重新开始
                if (b.value == null || v == n) 
                    break;
                 //和n节点的key对比,如果相等则返回值   
                if ((c = cpr(cmp, key, n.key)) == 0) {
                    @SuppressWarnings("unchecked") V vv = (V)v;
                    return vv;
                }
                //如果小于n节点的key则返回null( 因为key>p.key)
                if (c < 0)
                    break outer;
                //否则继续向后查询    
                b = n;
                n = f;
            }
        }
        return null;
    }

  

 

以上是关于ConcurrentSkipListMap源码分析的主要内容,如果未能解决你的问题,请参考以下文章

死磕 java集合之ConcurrentSkipListMap源码分析——发现个bug

JUC源码解析ConcurrentSkipListMap

集合-ConcurrentSkipListMap 源码解析

集合-ConcurrentSkipListMap 源码解析

Java多线程系列--“JUC集合”05之 ConcurrentSkipListMap

Java并发集合-ConcurrentSkipListMap分析和使用