ConcurrentSkipListMap 源码分析
ConcurrentSkipListMap 源码分析
ConcurrentSkipListMap
ConcurrentSkipListMap 是基于跳表实现的并发排序哈希表,映射可以根据键的自然顺序进行排序,
也可以根据创建映射时所提供的 Comparator 进行排序,具体取决于使用的构造方法。
ConcurrentSkipListMap 的 containsKey、get、put、remove 操作及其变体提供预期平均 log(n) 时间开销。
ConcurrentSkipListMap 的功能类似于 TreeMap 的线程安全版本。
创建实例
/**
* 执行键排序的比较器,如果以自然顺序排序则为 null
*/
final Comparator<? super K> comparator;
/** 延迟初始化的顶层索引 */
private transient Index<K,V> head;
/** 延迟初始化的元素计数 */
private transient LongAdder adder;
/** 延迟初始化的键集合 */
private transient KeySet<K,V> keySet;
/** 延迟初始化的值集合 */
private transient Values<K,V> values;
/** 延迟初始化的映射集合 */
private transient EntrySet<K,V> entrySet;
/** 延迟初始化的降序映射 */
private transient SubMap<K,V> descendingMap;
/**
* 头结点和标记节点的 key 为 null,在节点被删除时将 val 置为null
*/
static final class Node<K,V> {
final K key; // currently, never detached
V val;
Node<K,V> next;
Node(K key, V value, Node<K,V> next) {
this.key = key;
this.val = value;
this.next = next;
}
}
/**
* 索引节点以表示跳表的层级
*/
static final class Index<K,V> {
// 驻留节点值
final Node<K,V> node; // currently, never detached
// 下节点
final Index<K,V> down;
// 右节点
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;
}
}
/**
* 创建一个使用自然顺序进行排序的空 ConcurrentSkipListMap 实例
*/
public ConcurrentSkipListMap() {
this.comparator = null;
}
/**
* 创建一个使用比较器 comparator 进行排序的空 ConcurrentSkipListMap 实例
*/
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
添加元素
/**
* 1)如果目标 key 不存在,则写入新的键值对,并返回 null。
* 2)如果目标 key 存在,则替换其关联的值,并返回旧值。
*/
@Override
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) {
if (key == null) {
throw new NullPointerException();
}
final Comparator<? super K> cmp = comparator;
for (;;) {
/**
* h:head 头结点
* b:predecessor 前置节点
*/
Index<K,V> h; Node<K,V> b;
VarHandle.acquireFence();
// 节点所在的层次
int levels = 0;
// 1)head==null 表示第一次插入元素
if ((h = head) == null) { // try to initialize
// 创建一个标记节点
final Node<K,V> base = new Node<>(null, null, null);
// 创建索引节点
h = new Index<>(base, null, null);
// 更新头节点
b = ConcurrentSkipListMap.HEAD.compareAndSet(this, null, h) ? base : null;
}
// 2)跳表已经有元素存在
else {
/**
* q:index node
* r:right node
* d:down node
*/
for (Index<K,V> q = h, r, d;;) { // count while descending
// 索引节点的右节点不为 null
while ((r = q.right) != null) {
Node<K,V> p; K k;
/**
* 右索引节点的驻留节点为 null ||
* 节点的键为 null ||
* 节点的值为 null
*/
if ((p = r.node) == null || (k = p.key) == null ||
p.val == null) {
// 删除接地啊 q 的右侧节点
ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, r.right);
// 2)查找键大于当前节点键,则继续往右侧查找
} else if (ConcurrentSkipListMap.cpr(cmp, key, k) > 0) {
q = r;
// 3)查找键小于等于当前键,则当层节点已经查找完毕,往下层查找
} else {
break;
}
}
// 1)当前索引节点存在下层节点,则往下层查找
if ((d = q.down) != null) {
// 递增层级
++levels;
// 读取下层节点,重新进入循环并尝试往右侧查找
q = d;
}
else {
/**
* 2)已经到达最后一层
* 则读取驻留其上的节点值,开始玩右侧遍历
*/
b = q.node;
break;
}
}
}
// 前置节点存在
if (b != null) {
Node<K,V> z = null; // new node, if inserted
for (;;) {
/**
* n:node
* k:key
* v:value
* c:Comparisons 比较值
*/
Node<K,V> n, p; K k; V v; int c;
// 1)前置节点的 next 为 null,则表示当前节点是最后一个节点
if ((n = b.next) == null) {
if (b.key == null) {
ConcurrentSkipListMap.cpr(cmp, key, key);
}
c = -1;
}
// 2)节点键为 null
else if ((k = n.key) == null) {
break; // can't append; restart
// 3)节点值为 null,该节点已经被删除,需要从链表中踢除
} else if ((v = n.val) == null) {
ConcurrentSkipListMap.unlinkNode(b, n);
c = 1;
}
// 4)比较查找键和当前键,并且查找键比较大
else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
// 查找下一个节点
b = n;
/**
* 5)如果键相等 && 只有不存在才插入元素,则直接返回;否则尝试更新节点值
*/
} else if (c == 0 &&
(onlyIfAbsent || ConcurrentSkipListMap.VAL.compareAndSet(n, v, value))) {
// 更新成功则返回旧值
return v;
}
/**
* 目标键大于 predecessor.key 小于 predecessor.next.key,
* 将目标键值对插入到他们中间。
*/
if (c < 0 &&
ConcurrentSkipListMap.NEXT.compareAndSet(b, n,
p = new Node<>(key, value, n))) {
// 读取新增节点
z = p;
break;
}
}
if (z != null) {
// 读取随机数
final int lr = ThreadLocalRandom.nextSecondarySeed();
// 有 1/4 的机会基于新增节点生成索引节点
if ((lr & 0x3) == 0) { // add indices with 1/4 prob
final int hr = ThreadLocalRandom.nextSecondarySeed();
long rnd = (long)hr << 32 | lr & 0xffffffffL;
// 新增节点所在的层级,层级从 0 开始
int skips = levels; // levels to descend before add
Index<K,V> x = null;
for (;;) { // create at most 62 indices
// 基于新增节点生成顶层索引节点
x = new Index<>(z, x, null);
if (rnd >= 0L || --skips < 0) {
break;
} else {
rnd <<= 1;
}
}
/**
* 新增索引节点成功 &&
* 如果是新增顶层索引节点 &&
* 增加新的一层
*/
if (ConcurrentSkipListMap.addIndices(h, skips, x, cmp) && skips < 0 &&
head == h) { // try to add new level
// 将新增节点加到顶层
final Index<K,V> hx = new Index<>(z, x, null);
// 创建新的头节点
final Index<K,V> nh = new Index<>(h.node, h, hx);
// 更新头节点
ConcurrentSkipListMap.HEAD.compareAndSet(this, h, nh);
}
if (z.val == null)
{
findPredecessor(key, cmp); // clean
}
}
// 增加计数值
addCount(1L);
return null;
}
}
}
}
/**
* 在插入元素之后新增索引节点,从高层向低层递归插入,之后建立和前置节点的链接
* Recursion depths are exponentially less probable.
*
* @param q 当前层级的起始索引
* @param skips 插入索引时,需要跳过的层级数
* @param x 插入的目标索引
* @param cmp comparator
*/
static <K,V> boolean addIndices(Index<K,V> q, int skips, Index<K,V> x,
Comparator<? super K> cmp) {
Node<K,V> z; K key;
/**
* 1)新增索引节点不为 null &&
* 2)驻留数据节点不为 null &&
* 3)数据节点的键不为 null &&
* 4)起始索引节点不为 null
*/
if (x != null && (z = x.node) != null && (key = z.key) != null &&
q != null) { // hoist checks
boolean retrying = false;
for (;;) { // find splice point
/**
* r:right
* d:down
* c:Comparisons
*/
Index<K,V> r, d; int c;
/**
* 当前节点的右侧节点不为 null
*/
if ((r = q.right) != null) {
Node<K,V> p; K k;
// 1)尝试删除索引节点
if ((p = r.node) == null || (k = p.key) == null ||
p.val == null) {
ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, r.right);
c = 0;
}
// 2)目标键比当前节点键大
else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
// 往右侧查找
q = r;
// 3)目标键和当前键相等
} else if (c == 0) {
break; // stale
}
// 4)目标键比当前节点键小?
} else {
// 已经不存在右侧节点
c = -1;
}
if (c < 0) {
// 下节点不为 null && 层级数 > 0
if ((d = q.down) != null && skips > 0) {
// 递减层级后,往下查找
--skips;
q = d;
}
/**
* 1)下节点不为 null && skip <=0 && 未出现索引添加失败 &&
* 尝试在当前层级添加索引
*/
else if (d != null && !retrying &&
!ConcurrentSkipListMap.addIndices(d, 0, x.down, cmp)) {
// 索引添加失败则退出
break;
} else {
x.right = r;
// 插入新增索引节点
if (ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, x)) {
// 执行成功则退出
return true;
}
else {
retrying = true; // re-find splice point
}
}
}
}
}
return false;
}
读取元素
/**
* 返回指定 key 映射的值,如果 key 不存在,则返回 null
*/
@Override
public V get(Object key) {
return doGet(key);
}
/**
* Gets value for key. Same idea as findNode, except skips over
* deletions and markers, and returns first encountered value to
* avoid possibly inconsistent rereads.
*/
private V doGet(Object key) {
Index<K,V> q;
VarHandle.acquireFence();
if (key == null) {
throw new NullPointerException();
}
final Comparator<? super K> cmp = comparator;
V result = null;
if ((q = head) != null) {
outer: for (Index<K,V> r, d;;) {
/**
* 首先向右侧查找,之后向下查找
*/
while ((r = q.right) != null) {
Node<K,V> p; K k; V v; int c;
// 节点已经被删除
if ((p = r.node) == null || (k = p.key) == null ||
(v = p.val) == null) {
ConcurrentSkipListMap.RIGHT.compareAndSet(q, r, r.right);
// 目标键 > 节点键,则向右侧查找
} else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
q = r;
// 找到目标键
} else if (c == 0) {
// 读取值后返回
result = v;
break outer;
} else {
break;
}
}
// 尝试向下层查找
if ((d = q.down) != null) {
q = d;
} else {
// 已经到达底层
Node<K,V> b, n;
// 读取索引节点驻留的数据节点之后,往右侧遍历查找
if ((b = q.node) != null) {
while ((n = b.next) != null) {
V v; int c;
final K k = n.key;
// 跳过被删除节点和标记节点,如果目标键 > 节点键,也向右侧查找
if ((v = n.val) == null || k == null ||
(c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
b = n;
// 目标键 <= 节点键
} else {
// 如果相等,则直接返回其值
if (c == 0) {
result = v;
}
// 不存在相等的键,则返回 null
break;
}
}
}
break;
}
}
}
return result;
}
/**
* 返回目标 key 关联的值,如果键不存在,则返回 defaultValue
*/
@Override
public V getOrDefault(Object key, V defaultValue) {
V v;
return (v = doGet(key)) == null ? defaultValue : v;
}
替换值
/**
* 如果目标 key 存在,则替换其值为 value,并返回旧值;否则返回 null。
*/
@Override
public V replace(K key, V value) {
// 键和值都非 null
if (key == null || value == null) {
throw new NullPointerException();
}
for (;;) {
Node<K,V> n; V v;
// 没找到目标键
if ((n = findNode(key)) == null) {
// 则返回 null
return null;
}
// 尝试原子设置值,并返回旧值
if ((v = n.val) != null && ConcurrentSkipListMap.VAL.compareAndSet(n, v, value)) {
return v;
}
}
}
/**
* 返回指定 key 映射的节点,如果键不存在,则返回 null。
* 查找过程中会踢除已经被删除键值对的索引节点,并且重新通过 findPredecessor 方法
* 查找基础节点进行再次遍历【如果节点的 key 为 null,则表示它是一个标记节点,
* 它的前置节点将被删除。】
* The traversal loops in doPut, doRemove, and findNear all include the same checks.
*/
private Node<K,V> findNode(Object key) {
if (key == null)
{
throw new NullPointerException(); // don't postpone errors
}
final Comparator<? super K> cmp = comparator;
Node<K,V> b;
// 读取底层有效的前置节点
outer: while ((b = findPredecessor(key, cmp)) != null) {
for (;;) {
Node<K,V> n; K k; V v; int c;
// 1)已经到达尾部,则直接退出
if ((n = b.next) == null) {
break outer; // empty
// 2)当前节点是标记节点
} else if ((k = n.key) == null) {
break; // b is deleted
// 3)当前索引节点驻留的数据节点关联的键值对已经被删除,则踢除当前节点
} else if ((v = n.val) == null) {
ConcurrentSkipListMap.unlinkNode(b, n); // n is deleted
// 4)目标键 > 节点键,则往右侧查找
} else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
b = n;
// 5)找到目标节点,则直接返回
} else if (c == 0) {
return n;
} else {
break outer;
}
}
}
return null;
}
/**
* 删除节点 n
*/
static <K,V> void unlinkNode(Node<K,V> b, Node<K,V> n) {
if (b != null && n != null) {
Node<K,V> f, p;
for (;;) {
if ((f = n.next) != null && f.key == null) {
p = f.next; // already marked
break;
}
// 被删除节点是尾节点
else if (ConcurrentSkipListMap.NEXT.compareAndSet(n, f,
new Node<>(null, null, f))) {
p = f; // add marker
break;
}
}
ConcurrentSkipListMap.NEXT.compareAndSet(b, n, p);
}
}
/**
* 如果目标 key 存在 && 旧值为 oldValue,则替换其值为 value,替换成功返回 true
*/
@Override
public boolean replace(K key, V oldValue, V newValue) {
if (key == null || oldValue == null || newValue == null) {
throw new NullPointerException();
}
for (;;) {
Node<K,V> n; V v;
// 节点不存在
if ((n = findNode(key)) == null) {
return false;
}
if ((v = n.val) != null) {
// 节点值和 oldValue 不相等
if (!oldValue.equals(v)) {
return false;
}
// 原子更新旧值
if (ConcurrentSkipListMap.VAL.compareAndSet(n, v, newValue)) {
return true;
}
}
}
}
删除键值对
/**
* 如果指定的 key 存在,则删除键值对,并返回旧值;否则返回 null
*/
@Override
public V remove(Object key) {
return doRemove(key, null);
}
/**
* 定位节点,将其值置为 null,在其后添加一个删除标记节点,断开前置节点,
* 移除关联的索引节点,并尝试递减层级。
*/
final V doRemove(Object key, Object value) {
if (key == null) {
throw new NullPointerException();
}
final Comparator<? super K> cmp = comparator;
V result = null;
Node<K,V> b;
outer: while ((b = findPredecessor(key, cmp)) != null &&
result == null) {
for (;;) {
Node<K,V> n; K k; V v; int c;
// 1)无后继节点
if ((n = b.next) == null) {
break outer;
// 2)当前节点是一个删除标记节点
} else if ((k = n.key) == null) {
break;
// 3)当前节点的数据节点被删除,则将其踢除
} else if ((v = n.val) == null) {
ConcurrentSkipListMap.unlinkNode(b, n);
// 4)目标键 > 节点键,则往右侧查找
} else if ((c = ConcurrentSkipListMap.cpr(cmp, key, k)) > 0) {
b = n;
// 5)无匹配键,则直接返回
} else if (c < 0) {
break outer;
// 6)如果值匹配,则删除的场景
} else if (value != null && !value.equals(v)) {
break outer;
// 7)将节点值置为 null
} else if (ConcurrentSkipListMap.VAL.compareAndSet(n, v, null)) {
// 读取旧值
result = v;
// 踢除节点
ConcurrentSkipListMap.unlinkNode(b, n);
break; // loop to clean up
}
}
}
if (result != null) {
// 尝试递减层级
tryReduceLevel();
addCount(-1L);
}
return result;
}
/**
* 如果指定的 key 存在 && 键关联的值和 value 相等,则删除键值对,并返回 true。
*/
@Override
public boolean remove(Object key, Object value) {
if (key == null) {
throw new NullPointerException();
}
return value != null && doRemove(key, value) != null;
}
版权声明:本文为zhuxudong原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。