JDK源码中的跳表实现类: ConcurrentSkipListMap和ConcurrentSkipListSet。

其中ConcurrentSkipListSet的实现是基于ConcurrentSkipListMap。因此下面具体分析ConcurrentSkipListMap的实现:

//查找指定Key的前置节点
private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) { if (key == null) throw new NullPointerException(); // don't postpone errors for (;;) { for (Index<K,V> q = head, r = q.right, d;;) { if (r != null) { Node<K,V> n = r.node; K k = n.key; if (n.value == null) { if (!q.unlink(r)) // 如果节点r=q.right为空,则删除该节点r,即把节点q.right指向r.right. break; // restart 然后跳出本次循环,从头节点开始继续循环。 r = q.right; // reread r continue; } if (cpr(cmp, key, k) > 0) {// 通过Key值于当前节点的right节点比较,如果Key值较大,则继续往右比较 q = r; r = r.right; continue; } } if ((d = q.down) == null) // 如果当前节点的down为空,则当前链表为最底层链表,该节点的值<=key,此即为查询结果。 return q.node; q = d; // 如果Key值不比当前节点的right节点大,则继续往下比较 r = d.right; } } } // 根据Key查找对应的节点 private Node<K,V> findNode(Object key) { if (key == null) throw new NullPointerException(); // don't postpone errors Comparator<? super K> cmp = comparator; outer: for (;;) { for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {//从key的前置节点开始查找 Object v; int c; if (n == null) break outer; Node<K,V> f = n.next; if (n != b.next) // inconsistent read 读写不一致,重新开始查找 break; if ((v = n.value) == null) { // n is deleted 下一个节点为null,则删除该节点,重新开始查找 n.helpDelete(b, f); break; } if (b.value == null || v == n) // b is deleted break; if ((c = cpr(cmp, key, n.key)) == 0) //查找到,则返回结果 return n; if (c < 0) break outer; b = n; n = f; } } return null; }

private V doGet(Object key) 方法的实现与findNode一致,只是返回值为Value的复制。

新增一个节点的过程如下:

/**

     * 

     * Main insertion method.  Adds element if not present, or

     * replaces value if present and onlyIfAbsent is false.

     * @param key the key

     * @param value the value that must be associated with key

     * @param onlyIfAbsent if should not insert if already present

     * @return the old value, or null if newly inserted

     * 新增一个节点过程:

     * 1,根据新增的节点key值,寻找其合适的插入位置b;

     * 2,如果存在相等的key值,则根据onlyIfAbsent决定是否更新对应的Value值,然后返回;

     * 3,如果不存在相等的key值,则创建一个新的节点,并插入到合适的位置b,此时操作的是跳表的最底层;

     * 4,根据随机函数,决定是否添加上层节点,如果不需要添加,则直接返回null;

     * 5,如果需要添加上层节点,则获取随机值level;

     * 6,如果随机值level不大于当前最大层数,则创建一个从第一层到第level层的新的节点Index链表,其通过down指针连接,right指针都设置为null;

     * 7,如果随机值level大于当前最大层数,则跳表的最大层数加1,然后创建一个从第一层到新的最大层的新的节点Index链表,其通过down指针连接,right指针都设置

     *    为null;然后从旧的最大层数+1到新的最大层数间新增head节点链表,其通过down指针连接,right指针指向刚新增的对应层的Index节点;

     * 8,从旧的最大层数开始往最底层,把新增的index节点插入到合适的位置,即更新其right指针(完善第6步的操作)。至此,完成新增节点的整个过程。

     */

    private V doPut(K key, V value, boolean onlyIfAbsent) {

        Node<K,V> z;             // added node

        if (key == null)

            throw new NullPointerException();

        Comparator<? super K> cmp = comparator;

        outer: for (;;) {

            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {// 根据Key找到前置节点,然后开始查找

                if (n != null) {

                    Object v; int c;

                    Node<K,V> f = n.next;

                    if (n != b.next)               // inconsistent read

                        break;

                    if ((v = n.value) == null) {   // n is deleted

                        n.helpDelete(b, f);

                        break;

                    }

                    if (b.value == null || v == n) // b is deleted

                        break;

                    if ((c = cpr(cmp, key, n.key)) > 0) { //继续往右查找

                        b = n;

                        n = f;

                        continue;

                    }

                    if (c == 0) {

                        if (onlyIfAbsent || n.casValue(v, value)) {//如果存在key相等的节点,则如果onlyIfAbsent=false,则通过casValue更新Key对应的Value值。如果onlyIfAbsent=true,则不更新Key对应的Value值,然后返回oldValue。

                            @SuppressWarnings("unchecked") V vv = (V)v;

                            return vv;

                        }

                        break; // restart if lost race to replace value

                    }

                    // else c < 0; fall through

                }

                z = new Node<K,V>(key, value, n);  // 没有查找到对应的Key节点,则新增一个节点

                if (!b.casNext(n, z))  // 把新增的节点z设为当前节点的next节点;原子操作,失败则不断的循环操作

                    break;         // restart if lost race to append to b

                break outer;

            }

        }

        int rnd = ThreadLocalRandom.nextSecondarySeed();

        if ((rnd & 0x80000001) == 0) { // test highest and lowest bits   

                                       //8000000001 = 1000 0000 0000 0000 0000 0000 0000 0001 测试最高位和最低位是否为0

            int level = 1, max;

            while (((rnd >>>= 1) & 1) != 0)   //无符号右移1位,   随机获得level值

                ++level;

            Index<K,V> idx = null;

            HeadIndex<K,V> h = head;

            if (level <= (max = h.level)) {

                for (int i = 1; i <= level; ++i)

                    idx = new Index<K,V>(z, idx, null);

            }

            else { // try to grow by one level   使整个跳表的level增长1

                level = max + 1; // hold in array and later pick the one to use

                @SuppressWarnings("unchecked")Index<K,V>[] idxs =

                    (Index<K,V>[])new Index<?,?>[level+1];

                for (int i = 1; i <= level; ++i)

                    idxs[i] = idx = new Index<K,V>(z, idx, null); //包含如下两步操作

                    //idx = new Index<K,V>(z,idx,null);  设置idx值,并设置其down和right值

                    //idxs[i] = idx;                     设置每一层中新增的Index节点,其right值都设为null,down值设置为其下一层的Index节点。

                for (;;) {

                    h = head;

                    int oldLevel = h.level;

                    if (level <= oldLevel) // lost race to add level

                        break;

                    HeadIndex<K,V> newh = h;

                    Node<K,V> oldbase = h.node;

                    for (int j = oldLevel+1; j <= level; ++j)         //设置新增的Head节点,设置其node,down,right和level值

                        newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);

                    if (casHead(h, newh)) {        //更新head值成功,则退出无限循环

                        h = newh;                  //h 为新的跳表的head节点

                        idx = idxs[level = oldLevel];   //新增的层中,包含Head和idxs[max]两个节点,其指向关系已经确定,而oldLevel中,还没有设置idxs[level]的前置节点,因此idx = idxs[level = oldLevel],说明需要从此层开始至最底层,设置好idxs[level]的前置节点,下面的代码splice完成该功能。

                        break;

                    }

                }

            }

            // find insertion points and splice in

            splice: for (int insertionLevel = level;;) {

                int j = h.level;

                for (Index<K,V> q = h, r = q.right, t = idx;;) {

                    if (q == null || t == null)

                        break splice;

                    if (r != null) {

                        Node<K,V> n = r.node;

                        // compare before deletion check avoids needing recheck

                        int c = cpr(cmp, key, n.key);//key 根当前node.key比较

                        if (n.value == null) {

                            if (!q.unlink(r))

                                break;

                            r = q.right;

                            continue;

                        }

                        if (c > 0) {  //继续往右查找

                            q = r;

                            r = r.right;

                            continue;

                        }

                    }

                    if (j == insertionLevel) {

                        if (!q.link(r, t))     // 把节点t插入到q和r之间,t即新增的节点idx[level]

                            break; // restart

                        if (t.node.value == null) {

                            findNode(key);

                            break splice;

                        }

                        if (--insertionLevel == 0)//层数往下,如果已到最底层,则退出,最底层的节点值在之前的代码中已经完成插入。

                            break splice;

                    }

                    if (--j >= insertionLevel && j < level)

                        t = t.down; //t值更新,t即新增的节点idx[level]

                    q = q.down;

                    r = q.right;

                }

            }

        }

        return null;

    }

删除一个节点:

/**

     * Main deletion method. Locates node, nulls value, appends a

     * deletion marker, unlinks predecessor, removes associated index

     * nodes, and possibly reduces head index level.

     *

     * Index nodes are cleared out simply by calling findPredecessor.

     * which unlinks indexes to deleted nodes found along path to key,

     * which will include the indexes to this node.  This is done

     * unconditionally. We can't check beforehand whether there are

     * index nodes because it might be the case that some or all

     * indexes hadn't been inserted yet for this node during initial

     * search for it, and we'd like to ensure lack of garbage

     * retention, so must call to be sure.

     *

     * @param key the key

     * @param value if non-null, the value that must be

     * associated with key

     * @return the node, or null if not found

     */

    final V doRemove(Object key, Object value) {

        if (key == null)

            throw new NullPointerException();

        Comparator<? super K> cmp = comparator;

        outer: for (;;) {

            for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {

                Object v; int c;

                if (n == null)

                    break outer;

                Node<K,V> f = n.next;

                if (n != b.next)                    // inconsistent read

                    break;

                if ((v = n.value) == null) {        // n is deleted

                    n.helpDelete(b, f);

                    break;

                }

                if (b.value == null || v == n)      // b is deleted

                    break;

                if ((c = cpr(cmp, key, n.key)) < 0)  

                    break outer;

                if (c > 0) {

                    b = n;

                    n = f;

                    continue;

                }

                if (value != null && !value.equals(v))  //如果value不相等,退出

                    break outer;

                if (!n.casValue(v, null))    //无限循环,直至设置节点的值为null成功,

                    break;

                 //之前已经把当前节点值设为null,之后的删除操作分两步:1,在n和n.next间插入一个删除标记节点marker;

                // 2,设置b.next为f;这是由两个原子操作共同完成,如果都正常完成,则直接返回;如果有其中一步失败,则调用findNode(key)来继续完成删除null节点的操作;

                if (!n.appendMarker(f) || !b.casNext(n, f))   

                    findNode(key);          // retry via findNode

                else { .

                    findPredecessor(key, cmp);      // clean index

                    if (head.right == null)

                        tryReduceLevel(); //最上面三层都无索引节点,则把最上面一层的索引删除。

                }

                @SuppressWarnings("unchecked") V vv = (V)v;

                return vv;

            }

        }

        return null;

    }

         /**

         * 添加一个删除标记节点,设置当前节点的next节点为new Node(f),该新增节点的value值为当前节点f.value=f;

         * Tries to append a deletion marker to this node.

         * @param f the assumed current successor of this node

         * @return true if successful

         */

        boolean appendMarker(Node<K,V> f) {

            return casNext(f, new Node<K,V>(f));

        }

         Node(Node<K,V> next) {

            this.key = null;

            this.value = this;

            this.next = next;

        }

         /**

         * compareAndSet next field

         */

        boolean casNext(Node<K,V> cmp, Node<K,V> val) {

            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);

        }

         /**

         * 继续完成删除节点过程:

         * Helps out a deletion by appending marker or unlinking from

         * predecessor. This is called during traversals when value

         * field seen to be null.

         * @param b predecessor

         * @param f successor

         */

        void helpDelete(Node<K,V> b, Node<K,V> f) {

            /*

             * Rechecking links and then doing only one of the

             * help-out stages per call tends to minimize CAS

             * interference among helping threads.

             */

            if (f == next && this == b.next) {

                if (f == null || f.value != f) // not already marked 判断是否为marker节点(f.value=f)

                    casNext(f, new Node<K,V>(f));

                else

                    b.casNext(this, f.next);

            }

        }

通过源代码具体分析其删除步骤:

1, 删除前,需要删除节点n:
      

2,删除时,先设置n.value= null; 无限循环,直至成功为止;
3,添加删除标记节点marker:
marker 节点的key=null, value=f, next=f;
4,删除该节点n以及标记节点marker:
 
其中步骤2,3,4分别由3个CAS原子操作完成。步骤2保证成功,步骤3或者4只要有一个失败,此时的n.value = null, 因此都可以由节点遍历的操作来继续推进删除过程。
为什么删除操作要分为3步,而不是由一个步骤(步骤4)来完成?因为CAS操作只能保证一个变量操作的原子性。
 

JDK的跳表源码分析的更多相关文章

  1. JDK动态代理实现源码分析

    JDK动态代理实现方式 在Spring框架中经典的AOP就是通过动态代理来实现的,Spring分别采用了JDK的动态代理和Cglib动态代理,本文就来分析一下JDK是如何实现动态代理的. 在分析源码之 ...

  2. Redis学习之zskiplist跳跃表源码分析

    跳跃表的定义 跳跃表是一种有序数据结构,它通过在每个结点中维持多个指向其他结点的指针,从而达到快速访问其他结点的目的 跳跃表的结构 关于跳跃表的学习请参考:https://www.jianshu.co ...

  3. JDK 1.6 HashMap 源码分析

    前言 ​ 前段时间研究了一下JDK 1.6 的 HashMap 源码,把部份重要的方法分析一下,当然HashMap中还有一些值得研究得就交给读者了,如有不正确之处还望留言指正. 准备 ​ 需要熟悉数组 ...

  4. JDK 之 Arrays.asList - 源码分析

    Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List . 其源代码如下: @SafeVarargs public static <T> Lis ...

  5. JDK Collection 源码分析(2)—— List

    JDK List源码分析 List接口定义了有序集合(序列).在Collection的基础上,增加了可以通过下标索引访问,以及线性查找等功能. 整体类结构 1.AbstractList   该类作为L ...

  6. JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable

    JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...

  7. JDK源码分析(12)之 ConcurrentHashMap 详解

    本文将主要讲述 JDK1.8 版本 的 ConcurrentHashMap,其内部结构和很多的哈希优化算法,都是和 JDK1.8 版本的 HashMap是一样的,所以在阅读本文之前,一定要先了解 Ha ...

  8. JDK(五)JDK1.8源码分析【集合】HashMap

    本文转载自无始无终,原文连接 HashMap 在 JDK 1.8 后新增的红黑树结构 传统 HashMap 的缺点 JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也 ...

  9. C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库

    C# DateTime的11种构造函数   别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...

随机推荐

  1. bootstrap学习笔记细化(表单)

    主要属性: class="form-inline" 水平排列 class="form-group" 组键 form-control 圆角方框发光 input-l ...

  2. 02.CSS动画示例-->鼠标悬停图片旋转

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. oracle基础-创建表空间

    一. 创建表空间的完整格式 CREATE [UNDO|TEMPORARY] TABLESPACE tablespace_name       DATAFILE 'path/filename' [SIZ ...

  4. CentOS6.5(4)----宿主机无法访问虚拟机中的web服务解决方案

    宿主机无法访问虚拟机中的web服务 在Windows7宿主机中的VMware虚拟机中安装了CentOS6.5操作系统,并且基于Nginx搭建了Web服务器,网页刚刚搭建好的时候,通过宿主机的浏览器可以 ...

  5. 文件路径太长无法删除 robocopy

    方法一:新建空文件夹 D:\temp robocopy D:\temp D:\target\source\test  /purge 或者在同一目录下,建一个空文件夹, test 在cmd下切换进入到当 ...

  6. 学习ssm心得

    学习是一个探索的过程,在我编程能力提高的阶段中,我发现在编写代码之前,必须得弄清代码的逻辑,我到底要干什么,从哪一步做起.端与端之间该怎么衔接. 先把问题想清楚,再去编写.不要急着去乱敲一通,一点用处 ...

  7. Python json与pickle

    这是用于序列化的两个模块: • json: 用于字符串和python数据类型间进行转换 • pickle: 用于python特有的类型和python的数据类型间进行转换 Json模块提供了四个功能:d ...

  8. 了解 Azure VM 的系统重启

    有时 Azure 虚拟机 (VM) 可能重启,即使没有明显原因,也没有证据表明用户发起重启操作. 本文列出了可导致 VM 重启的操作和事件,并针对如何避免意外重启问题或减少该问题影响提供见解. 配置 ...

  9. select 1 from table 语句中的1代表什么意思

    在这里我主要讨论的有以下几个select 语句: doo_archive表是一个数据表,表的行数为4行,如下: 分别用三条select语句select  1 from doo_archive.sele ...

  10. Python学习---Java和Python的区别小记

    Java和Python的区别小记 注意这里使用的是 and/or/not  非java中的&&,||,!Java中的true是小写 Python中函数就是对象,函数和我们之前的[1,2 ...