[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的主要内容,如果未能解决你的问题,请参考以下文章