ConcurrentSkipListMap 源码全解

ConcurrentSkipListMap 源码全解

前面我们学习了 ConcurrentHashMap 的实现,这一篇我们来学习一下基于跳表实现的 Map 结合

核心成员介绍

private static final Object BASE_HEADER = new Object(); // 数据链表的头部特殊值
private transient volatile HeadIndex<K,V> head;// 指向跳表最顶层索引节点
final Comparator<? super K> comparator; // 用来比较 key 的比较器

public ConcurrentSkipListMap() {
    this.comparator = null;
    initialize();
}

private void initialize() {
    keySet = null;
    entrySet = null;
    values = null;
    descendingMap = null;
    // 初始化头部索引节点
    head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
                              null, null, 1);
}
// 封装 key - value 的节点类
static final class Node<K,V> {
    final K key;
    volatile Object value;
    volatile Node<K,V> next; // 单向链表结构

    Node(K key, Object value, Node<K,V> next) {
        this.key = key;
        this.value = value;
        this.next = next;
    }
	// 创建一个标记节点,此时 key == null,value 指向自己本身
    Node(Node<K,V> next) {
        this.key = null;
        this.value = this;
        this.next = next;
    }
}
// 每一层 Index 索引节点的 头结点
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;
    }
}
// 跳表索引节点,跳表的层级结构实现
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;
    }
}

核心方法介绍

put 添加数据

1、将数据插入数据节点链表,插入过程中如果发现节点被删除将帮助从链表中删除节点,如果发现该 key 已存在,根据 onlyIfAbsent 判断是否更新 value 值

2、通过随机数控制是否增加索引节点和索引层级,每次最多增加一层,效果如图
在这里插入图片描述

public V put(K key, V value) {
    return doPut(key, value, false);
}

private V doPut(K key, V value, boolean onlyIfAbsent) {
    Node<K,V> z;             // added node
    Comparator<? super K> cmp = comparator; // 默认 comparator = null
    outer: for (;;) {// 保证插入成功
        // 调用 findPredecessor 方法找到小于给定 key 的 node 节点
        for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
            if (n != null) {// 当前节点 b 存在 next 节点
                Object v; int c;
                Node<K,V> f = n.next; // 取出 n 的 next 节点
                // 节点发生了改变,退出当前循环,重复 outer 循环
                if (n != b.next)  break; 
                if ((v = n.value) == null) {   // n 节点被删除了,帮助移除跳表
                    /* helpDelete 方法
                    if (f == next && this == b.next) { // f 节点未发生改
                		if (f == null // 当前节点是最后一个节点 也即 n 是最后一个节点
                		|| f.value != f) // 判断 f 节点是否已被删除
                			casNext(f, new Node<K,V>(f)); // CAS 更新 n 节点 next 指向一个标记节点
                		else 
                			b.casNext(this, f.next); // 执行到这一步说明 f 节点也被删除了,那么将 b.next 指向 f.next
            		}
                    */
                    n.helpDelete(b, f);
                    break;
                }
                if (b.value == null || v == n) // 表明当前节点已被删除,退出当前循环,重复 outer 循环
                    break;
                if ((c = cpr(cmp, key, n.key)) > 0) {// 如果 当前 key 大于 n.key,那么更新 b、n 继续查找
                    b = n;
                    n = f;
                    continue;
                }
                if (c == 0) { // 表明跳表中存在相同 key 的节点,CAS 更新该节点的 value 值
                    if (onlyIfAbsent || n.casValue(v, value)) {
                        V vv = (V)v;
                        return vv;// 更新成功返回就值
                    }
                    break; 
                }
            }

            z = new Node<K,V>(key, value, n);// 创建新的 node 节点, z.next = n
            if (!b.casNext(n, z)) // cas 更新 b.next = z
                break;       
            break outer;
        }
    }
	// 索引节点的创建通过一个随机数 & 0x80000001 看看 高位和低位是否为 0 ,高位和低位同时为 0 时,则创建索引节点
    int rnd = ThreadLocalRandom.nextSecondarySeed();
    if ((rnd & 0x80000001) == 0) { 
        int level = 1, max; // 初始索引层级 level = 1
        while (((rnd >>>= 1) & 1) != 0) // 随机数 无符号 右移 1 位 检测末位是否为 0 ,为 0 退出循环,否则 level + 1
            ++level;
        Index<K,V> idx = null;
        HeadIndex<K,V> h = head;// 获取索引头结点
        // 默认 头结点的 head.level == 1,第一层索引,当 level == 1 时, 对当前 节点 z 创建索引节点
        if (level <= (max = h.level)) {
            for (int i = 1; i <= level; ++i)
                idx = new Index<K,V>(z, idx, null);
        }
        else { // 增加一层索引
            level = max + 1; // 当前索引层数 + 1
            Index<K,V>[] idxs = (Index<K,V>[])new Index<?,?>[level+1];
            // 创建 z 节点的 索引节点,上下每一层对应的 索引节点通过 down 进行关联
            // 次循环完成后效果如下    | index2 | --down--> | index1 |	 
            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) break;// 其它线程已经增加了索引层数,退出当前循环,也即发生了不一致读
                HeadIndex<K,V> newh = h;// 保存当前头结点
                Node<K,V> oldbase = h.node;// 获取头结点的 node 节点,也即 BASE_HEADER 伪节点
                // 创建索引头结点,其 node 指向 BASE_HEADER 伪节点,down 指向原来的头结点,right 指向 idx[j] 索引节点
                for (int j = oldLevel+1; j <= level; ++j)
                    newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
                if (casHead(h, newh)) {// cas 更新 head 执行新的头结点
                    h = newh;// 获取最新 头结点 给 h
                    idx = idxs[level = oldLevel]; // 保存 oldLevel 给到 level,获取 下一层的索引节点 给到 idx
                    break;
                }
            }
        }
        splice: for (int insertionLevel = level;;) {// 从最底层索引节点开始遍历
            int j = h.level;// 获取最新的索引层级 level 值 
            // 每一次 splice 循环都将从 等层索引开始遍历,找到索引节点插入位置
            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;// 获取索引节点的 数据节点 Node
                    int c = cpr(cmp, key, n.key);// 通过 compareTo 比较当前节点的 key 与 待插入的 key 的大小
                    if (n.value == null) {// 表示 节点已被删除
                        if (!q.unlink(r)) break;// 将该索引节点从链表中移除
                        r = q.right;// 重新获取 right 节点,重新查找
                        continue;
                    }
                    if (c > 0) {// 待插入 key 大于 当前找打的 key,更新 q,r 继续查找 
                        q = r;
                        r = r.right;
                        continue;
                    }
                }
			   // 上述判断帮助我们找到了插入点,下面开始插入操作
                if (j == insertionLevel) {// 头结点的层级等于待插入的层级
                    if (!q.link(r, t)) break; // 尝试将 t 插入 q 和 r 的中间
                    if (t.node.value == null) {// 节点被删除,调用findNode 方法删除节点,然后退出循环
                        findNode(key);
                        break splice;
                    }
                    if (--insertionLevel == 0) break splice;// insertionLevel == 0 表示索引节点插入完成
                }
			   // 从索引头结点往下寻找,找到正确的插入层级节点
                if (--j >= insertionLevel && j < level)
                    t = t.down;
                q = q.down; // 层级下移
                r = q.right;
            }
        }
    }
    return null;
}
findPredecessor 查找

找到小于给定 key 的数据节点 node 节点,也即找到 待插入起始位置;查找过程中发现右节点是删除状态,但是还未从链表中移除,帮助其完成删除操作

private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
    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))
                        break;           // restart
                    r = q.right;         // reread r
                    continue;
                }
                if (cpr(cmp, key, k) > 0) {
                    q = r;
                    r = r.right;
                    continue;
                }
            }
            if ((d = q.down) == null)
                return q.node;
            q = d;
            r = d.right;
        }
    }
}
get 查找数据

通过 索引节点 快速找到 数据查找起始位置,从该位置通过 next 向后遍历查找,直到找到该 key

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

private V doGet(Object key) {
    Comparator<? super K> cmp = comparator;
    outer: for (;;) {
        // 调用 findPredecessor 找到数据查找起始位置
        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) break; // 发生不一致读了
            if ((v = n.value) == null) {    // n 节点被删除了,帮助完成删除动作
                n.helpDelete(b, f);
                break;
            }
            if (b.value == null || v == n) break; // b 节点被删除,退出当前循环
            if ((c = cpr(cmp, key, n.key)) == 0) {// 找到了,返回即可
                V vv = (V)v;
                return vv;
            }
            if (c < 0) break outer;// 没有找到 返回 null
            b = n;
            n = f;
        }
    }
    return null;
}
remove 删除数据

通过 索引节点 快速找到 数据查找起始位置,从该位置通过 next 向后遍历查找,直到找到待删除数据,删除成功后将 当前节点的 next 指向一个标记节点,用于标记该节点已被删除

public V remove(Object key) {
    return doRemove(key, null);
}

final V doRemove(Object key, Object value) {
    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) break; // 不一致读
            if ((v = n.value) == null) {  // n 节点被删除了,帮助完成删除动作
                n.helpDelete(b, f);
                break;
            }
            if (b.value == null || v == n)  break;   // b 节点被删除了,帮助完成删除动作
            if ((c = cpr(cmp, key, n.key)) < 0) break outer; // 未找到节点
            if (c > 0) { // 更新 b、n继续查找
                b = n;
                n = f;
                continue;
            }
            if (value != null && !value.equals(v)) break outer;
            if (!n.casValue(v, null)) break;// 找到数据,CAS 删除
            // appendMarker 方法 casNext(f, new Node<K,V>(f)); 上一步 CAS 删除失败,尝试将 n 的 next 指向一个标记节点,用于标记节点被删除,标记成功,更新 b.next 指向 f
            if (!n.appendMarker(f) || !b.casNext(n, f)) 
                findNode(key);                  // 调用 findNode 删除节点
            else {
                findPredecessor(key, cmp);      // 删除 索引节点
                if (head.right == null) // 如果 头结点后面没有索引节点,尝试减少索引层级
                    tryReduceLevel();
            }
            V vv = (V)v;
            return vv; // 返回删除节点的 value
        }
    }
    return null;
}
 删除节点
            else {
                findPredecessor(key, cmp);      // 删除 索引节点
                if (head.right == null) // 如果 头结点后面没有索引节点,尝试减少索引层级
                    tryReduceLevel();
            }
            V vv = (V)v;
            return vv; // 返回删除节点的 value
        }
    }
    return null;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值