[JDK源码]-J.U.C-ConcurrentSkipListMap

Posted L._l

tags:

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

由于作者水平有限,如有什么错误点,多谢指出。

ConcurrentSkipListMap

/*头节点          	   索引节点(最底层保存的是 数据节点 其他层保存的是 索引节点)
* +-+    right        +-+                      +-+
* |2|---------------->| |--------------------->| |->null
* +-+                 +-+                      +-+
*  | down              |                        |
*  v                   v                        v
* +-+            +-+  +-+       +-+            +-+       +-+
* |1|----------->| |->| |------>| |----------->| |------>| |->null
* +-+            +-+  +-+       +-+            +-+       +-+
*  v              |    |         |              |         |
* Nodes  next     v    v         v              v         v
* +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+
* | |->|A|->|B|->|C|->|D|->|E|->|F|->|G|->|H|->|I|->|J|->|K|->null
*/
public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable 
    //链表头部的 特殊值
    private static final Object BASE_HEADER = new Object();
 
    // 跳表 最顶层索引
    private transient volatile HeadIndex<K,V> head;

    // 比较器 K按照什么比较
    final Comparator<? super K> comparator;

    /** Lazily initialized key set */ //懒加载的 几个集合
    private transient KeySet<K> keySet;
    /** Lazily initialized entry set */
    private transient EntrySet<K,V> entrySet;
    /** Lazily initialized values collection */
    private transient Values<V> values;
    /** Lazily initialized descending key set */
    private transient ConcurrentNavigableMap<K,V> descendingMap;
    //保存K-V的节点
    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;
        
        //创建一个新的标记节点。 标记的区别在于它的值字段指向它自己。  
        //创建新的标记节点,在删除节点时使用,注意这里的节点 k null v 指向它自己
        Node(Node<K,V> next) 
            this.key = null;
            this.value = this;
            this.next = next;
        
    
    // 跳表的索引节点, 层级结构
    static class Index<K,V> 
        final Node<K,V> node;
        final Index<K,V> down;
        volatile Index<K,V> right;
    
    //每层 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;
        
    
    //默认构造
    public ConcurrentSkipListMap() 
        this.comparator = null;
        initialize();
    

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);

put操作

public V put(K key, V value) 
    if (value == null)
        throw new NullPointerException();
    return doPut(key, value, false);

private V doPut(K key, V value, boolean onlyIfAbsent) 
    Node<K,V> z;             // 要添加的节点
    if (key == null)
        throw new NullPointerException();
    Comparator<? super K> cmp = comparator; //比较器
    outer: for (;;) //循环  直到插入node节点
        //找到 k要放的的位置的  前一个和后一个节点 b n
        for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) 
            if (n != null) 	//b后边还有节点
                Object v; int c;
                Node<K,V> f = n.next;	//n的下一个节点
                if (n != b.next)               // n不是 b的下一个节点了  重新开始 
                    break;
                if ((v = n.value) == null)    // n 被删除了, 帮助删除,重试
                    n.helpDelete(b, f);
                    break;
                
                if (b.value == null || v == n) // b被删除了
                    break;
                if ((c = cpr(cmp, key, n.key)) > 0) //当前k 和 n的k比较, 需不需要更新
                    b = n;
                    n = f;
                    continue;
                
                if (c == 0) 	//二者相等
                    //如果onlyIfAbsent 设置为true,不进行旧值替换,否则替换旧值并返回旧值
                    if (onlyIfAbsent || n.casValue(v, value)) 
                        @SuppressWarnings("unchecked") V vv = (V)v;
                        return vv;
                    
                    break; // 如果 失败,此时退出当前循环 重试 
                
            
			//找到了插入当前的位置
            z = new Node<K,V>(key, value, n);
            if (!b.casNext(n, z))
                break;         // CAS将b的next修改为z ,失败重试
            break outer;//退出外循环 完成插入
        
    
	// 插入成功  是否需要更新索引
    int rnd = ThreadLocalRandom.nextSecondarySeed(); //当前线程获取一个随机数
    if ((rnd & 0x80000001) == 0)  //10000000000000000000000000000001  & 随机数 :最高最低 是不是1
        int level = 1, max;
        while (((rnd >>>= 1) & 1) != 0)//当前节点应该处于的层级  (随机数右移 & 1) 
            ++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  // 当前层大于 head 的层 
            level = max + 1; // 当前level 设置为 原 + 1
            //创建层级索引 数组
            @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);
            for (;;) // 更新头节点
                h = head;	//原头节点
                int oldLevel = h.level;//原 层
                if (level <= oldLevel) // 如果已经 小于了 原层,表示其他线程更新了  退出
                    break;
                HeadIndex<K,V> newh = h;
                Node<K,V> oldbase = h.node;	//原头节点指向的 node
                for (int j = oldLevel+1; j <= level; ++j)//创建每一层 索引的头节点
                    // 头节点的 right 节点指向当前 idxs[j]   层数为j
                    newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
                if (casHead(h, newh)) //更新索引节点的 头节点
                    h = newh;	//h为最新的头节点
                    idx = idxs[level = oldLevel]; //当前索引节点为 level 下标处的节点
                    break;
                
            
        
        // 找到插入点并拼接进去	
        splice: for (int insertionLevel = level;;) //从最顶层 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) //q 是不是最后一个
                    Node<K,V> n = r.node;
                    // 比较当前K 和 right索引的 node节点的 V的值
                    int c = cpr(cmp, key, n.key);
                    if (n.value == null) //如果 n的V为空
                        if (!q.unlink(r))//q的 next指向 r的next  失败重试  成功 获取新的 right节点
                            break;
                        r = q.right;
                        continue;
                    
                    if (c > 0) 
                        q = r;	//更新q为右边索引
                        r = r.right;//r 更新为原来r 的 right 节点
                        continue;
                    
                
				//头节点的层 等于 待插入的 层
                if (j == insertionLevel) 
                    if (!q.link(r, t))// 节点插入q r 中间
                        break; // 失败重试
                    if (t.node.value == null) //node 节点被删除 调用findNode - 删除中间被删除的节点,然后停止索引节点插入
                        findNode(key);
                        break splice;
                    
                    //全部插入完成
                    if (--insertionLevel == 0)
                        break splice;
                
				//从 索引头节点往下寻找 找到正确的插入的 层
                if (--j >= insertionLevel && j < level)
                    t = t.down;
                q = q.down;	//层 下移
                r = q.right;//获取新的 right节点
            
        
    
    return null;

get方法

首先通过 head索引节点获取到层级的 头节点然后 这个节点找到 下一个索引节点 去判断

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 (;;) 
        //findPredecessor 找到小于给定K 的前一个节点  以及下一个节点 n
        for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) 
            Object v; int c;
            if (n == null)//b是最后一个节点 退出
                break outer;
            Node<K,V> f = n.next; 	//获取n的下一个节点
            if (n != b.next)                // b的next发生了改变
                break;
            if ((v = n.value) == null)     // n删除了
                n.helpDelete(b, f);
                break;
            
            if (b.value == null || v == n)  // b 删除了
                break;
            if ((c = cpr(cmp, key, n.key)) == 0) //比较n的k和传入的k, 如果相同返回 V值
                @SuppressWarnings("unchecked") V vv = (V)v;
                return vv;
            
            if (c < 0)//表示K 的值小于 n的值,表示链表中 不存在
                break outer;
            b = n;//往后继续查找
            n = f;
        
    
    return null;

//找到小于给定K的前一个节点 
private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) 
    if (key == null)
        throw new NullPointerException(); // don't postpone errors
    for (;;) 
        //索引节点的头节点 和 right 节点
        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)  //n被删除了
                    if (!q.unlink(r))	//尝试断开r
                        break;           
                    r = q.right;         // 读取新的右节点
                    continue;
                
                if (cpr(cmp, key, k) > 0)  // 当前K 大于节点 n的值,根据索引节点往后查找
                    q = r;
                    r = r.right;
                    continue;
                
    

以上是关于[JDK源码]-J.U.C-ConcurrentSkipListMap的主要内容,如果未能解决你的问题,请参考以下文章

JDK1.8源码下载及idea2021导入jdk1.8源码

关于JDK源码:我想聊聊如何更高效地阅读

关于JDK源码:我想聊聊如何更高效地阅读

idea 导入 jdk源码 解决compile code 后阅读jdk 源码

关于JDK源码:我想聊聊如何更高效地阅读.md

怎样读jdk源码