Jdk1.6 JUC源码解析(26)-ConcurrentSkipListMap、ConcurrentSkipListSet
作者:大飞
- ConcurrentSkipListMap是一种线程安全的有序的Map。一般我们使用有序Map,不要求线程安全的情况下,可以使用TreeMap,要求线程安全的话,就可以使用ConcurrentSkipListMap。
- ConcurrentSkipListMap内部的数据结构是SkipList(跳表),内部Entry顺序是由实现了Comparable的key或者构造时指定的Comparator来保证。和TreeMap一样,对ConcurrentSkipListMap中元素的put、get和remove等操作的平均时间复杂度也是O(log(n))。
- 在看内部结构之前,先对跳表这种数据结构有个感性的认识,贴个图:
* Head nodes Index nodes
* +-+ 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
* +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
- 看下源码中这些节点的结构表示:
/**
* 节点持有key和value,按顺序链接,单向链表。
* 中间可能会链接一些处于中间状态的标记节点。
* 链表头节点是一个哑(dummy)节点,可以通过head.node访问。
* value域之所以定义成Object(而不是E),是因为还要存放一些针对标记节点和头节点的特殊值(non-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;
}
/**
* 创建一个标记节点。
* 标记节点和普通节点的重要区别是:标记节点的value域指向自身,
* 同时标记节点的key为null。key是否为null在一些地方可以用来
* 区分标记节点,但无法区分标记节点和base-level链表头节点,
* 因为base-level链表头节点的key也是null。
*/
Node(Node<K,V> next) {
this.key = null;
this.value = this;
this.next = next;
}
/** Updater for casNext */
static final AtomicReferenceFieldUpdater<Node, Node>
nextUpdater = AtomicReferenceFieldUpdater.newUpdater
(Node.class, Node.class, "next");
/** Updater for casValue */
static final AtomicReferenceFieldUpdater<Node, Object>
valueUpdater = AtomicReferenceFieldUpdater.newUpdater
(Node.class, Object.class, "value");
/**
* compareAndSet value field
*/
boolean casValue(Object cmp, Object val) {
return valueUpdater.compareAndSet(this, cmp, val);
}
/**
* compareAndSet next field
*/
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
return nextUpdater.compareAndSet(this, cmp, val);
}
/**
* 判断节点是否为标记节点。
*/
boolean isMarker() {
return value == this;
}
/**
* 判断节点是否是base-level链表的头节点。
*/
boolean isBaseHeader() {
return value == BASE_HEADER;
}
/**
* 尝试在当前节点后面追加一个删除标记节点。
*/
boolean appendMarker(Node<K,V> f) {
return casNext(f, new Node<K,V>(f));
}
/**
* 通过追加一个删除标记节点或移除一个标记节点来推进删除。
*/
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
appendMarker(f);
else
b.casNext(this, f.next);
}
}
/**
* 获取合法的value值。
*/
V getValidValue() {
Object v = value;
if (v == this || v == BASE_HEADER)
//如果是标记节点或者base头节点,返回null。
return null;
return (V)v;
}
/**
* 为当前映射(Node)创建一个不变(不可修改)的快照。
* 如果没有合法值,返回null
*/
AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
V v = getValidValue();
if (v == null)
return null;
return new AbstractMap.SimpleImmutableEntry<K,V>(key, v);
}
}
1.假设当前节点为n,n的前驱节点为b,n的后继节点为f,如图:
+------+ +------+ +------+
... | b |------>| n |----->| f | ...
+------+ +------+ +------+
+------+ +------+ +------+ +------+
... | b |------>| n |----->|marker|------>| f | ...
+------+ +------+ +------+ +------+
3.接下来是删除步骤,直接将节点n和后面的标记节点一起删除,如图:
+------+ +------+
... | b |----------------------------------->| f | ...
+------+ +------+
上面是普通节点,再看下Index节点和Head节点:
/**
* Index节点表示跳表的层级。
* 注意到Node和Index都有正向的指针,但是它们的类型和作用都不同,
* 无法抽象到一个基类里面。
*/
static class Index<K,V> {
final Node<K,V> node;
final Index<K,V> down;
volatile Index<K,V> right;
/**
* Creates index node with given values.
*/
Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
this.node = node;
this.down = down;
this.right = right;
}
/** Updater for casRight */
static final AtomicReferenceFieldUpdater<Index, Index>
rightUpdater = AtomicReferenceFieldUpdater.newUpdater
(Index.class, Index.class, "right");
/**
* compareAndSet right field
*/
final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
return rightUpdater.compareAndSet(this, cmp, val);
}
/**
* 判断当前Index的Node节点是否被删除。
*/
final boolean indexesDeletedNode() {
return node.value == null;
}
/**
* 尝试设置新的后继节点。
*/
final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
Node<K,V> n = node;
newSucc.right = succ;
//需要先检测当前Index的Node是否被删除。
return n.value != null && casRight(succ, newSucc);
}
/**
* 尝试设置后继节点(right)为后继的后继(越过后继节点)
*/
final boolean unlink(Index<K,V> succ) {
return !indexesDeletedNode() && casRight(succ, succ.right);
}
}
/**
* 头节点,每个头节点都包含一个表示层级的域。
*/
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 class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>
implements ConcurrentNavigableMap<K,V>,
Cloneable,
java.io.Serializable {
private static final long serialVersionUID = -8627078645895051609L;
/**
* 用来生成种子的随机数生成器。
*/
private static final Random seedGenerator = new Random();
/**
* 用来定义base-level的头结点。
*/
private static final Object BASE_HEADER = new Object();
/**
* 跳表最高层的head index
*/
private transient volatile HeadIndex<K,V> head;
/**
* 比较器。如果没设置这个比较器,那么久用key的自然序来比较。
* @serial
*/
private final Comparator<? super K> comparator;
/**
* 随机种子,这里没有用volatile修饰,多个线程看到不同的值也没关系。
*/
private transient int randomSeed;
- 大体了解了内部结构,接下来先从简单的构造方法入手分析:
public ConcurrentSkipListMap() {
this.comparator = null;
initialize();
}
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
this.comparator = comparator;
initialize();
}
两个构造方法除了指定比较器的区别外,都调用了initialize方法,看下这个方法:
/**
* 初始化或重置内部状态。
*/
final void initialize() {
//将内部一些域置空。
keySet = null;
entrySet = null;
values = null;
descendingMap = null;
//生成随机种子,这个种子用来生成随机的Level。
randomSeed = seedGenerator.nextInt() | 0x0100; // 确保非0
//生成头节点,该节点value是BASE_HEADER,level是1。
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 kkey, V value, boolean onlyIfAbsent) {
//将原本的key转化成一个可比较的key。
Comparable<? super K> key = comparable(kkey);
for (;;) {
//通过key找到要插入位置的前驱节点(注意这个节点在base_level上)
Node<K,V> b = findPredecessor(key);
Node<K,V> n = b.next;
for (;;) {
if (n != null) {
Node<K,V> f = n.next;
if (n != b.next) //检测一下,如果读取不一致,说明发生竞争,重试。
break;;
Object v = n.value;
if (v == null) { //节点n已经被删除了
n.helpDelete(b, f); //删除动作
break; //重试。
}
if (v == n || b.value == null) //节点b被删除
break; //重试。
int c = key.compareTo(n.key);
if (c > 0) {
//如果c>0,说明当前的节点应该排在n的后面,所以从n后面继续比较。
b = n;
n = f;
continue;
}
if (c == 0) {
//如果onlyIfAbsent为true,那么不进行替换;
//否则需要覆盖旧值。
if (onlyIfAbsent || n.casValue(v, value))
return (V)v;
else
break; // 覆盖时竞争失败,重试。
}
// else c < 0; fall through
}
//1.构造一个新节点。
Node<K,V> z = new Node<K,V>(kkey, value, n);
//2.尝试插入b和n之间。
if (!b.casNext(n, z))
break; // 如果尝试插入失败,重试一次。
//插入成功后,随机生成一个层级。(这个level不会超过31)
int level = randomLevel();
if (level > 0)
//level大于0,插入index
insertIndex(z, level);
return null;
}
}
}
这里看一下doPut方法中转化key使用的方法:
private Comparable<? super K> comparable(Object key) throws ClassCastException {
if (key == null)
throw new NullPointerException();
if (comparator != null)
return new ComparableUsingComparator<K>((K)key, comparator);
else
return (Comparable<? super K>)key;
}
static final class ComparableUsingComparator<K> implements Comparable<K> {
final K actualKey;
final Comparator<? super K> cmp;
ComparableUsingComparator(K key, Comparator<? super K> cmp) {
this.actualKey = key;
this.cmp = cmp;
}
public int compareTo(K k2) {
return cmp.compare(actualKey, k2);
}
}
private void addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) {
// 记录下一个要添加的level,以防重试。
int insertionLevel = indexLevel;
Comparable<? super K> key = comparable(idx.node.key);
if (key == null) throw new NullPointerException();
// 和findPredecessor过程类似,只是在过程中会添加index节点。
for (;;) {
int j = h.level;
Index<K,V> q = h;
Index<K,V> r = q.right;
Index<K,V> t = idx;
for (;;) {
if (r != null) {
Node<K,V> n = r.node;
// compare before deletion check avoids needing recheck
int c = key.compareTo(n.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) {
// 这里还需要检查t节点是否被删除,如果t节点被删除,就不能插入。
if (t.indexesDeletedNode()) {
findNode(key); // cleans up
return;
}
//尝试将Index t插入q和q的right节点r之间。
if (!q.link(r, t))
break; // restart
if (--insertionLevel == 0) {
// 最后还要做一次删除检测。
if (t.indexesDeletedNode())
findNode(key);
return;
}
}
if (--j >= insertionLevel && j < indexLevel)
t = t.down;
q = q.down;
r = q.right;
}
}
}
看到上述put的第2步过程,可能会疑惑,这是一个一个往后找的,put的时间复杂度看起来更像O(n)啊,其实玄机就在findPredecessor方法里面,看下这个方法:
/**
* 返回最底层(base-level)节点链中比给定key小(在“给定节点”左边)的节点,
* 如果没找到,那么返回底层链的头节点。
* 在查找过程中会顺手删除帮助删除一点标记为删除的节点。
*/
private Node<K,V> findPredecessor(Comparable<? super K> key) {
if (key == null)
throw new NullPointerException(); // don't postpone errors
for (;;) {
//将最高层的头节点赋给q。
Index<K,V> q = head;
//将最高层头结点的右节点赋给r。
Index<K,V> r = q.right;
for (;;) {
if (r != null) {
//如果r不为null,找到r中的数据节点n。
Node<K,V> n = r.node;
K k = n.key;
if (n.value == null) {
//如果n已经被删除,那么尝试推进删除。
if (!q.unlink(r))
break; // 推进删除失败,重试。
r = q.right; // 再次读取q的头结点,因为上面删除成功后,q的右节点变了。
continue;
}
//n没被删除的话,和key进行比较。
if (key.compareTo(k) > 0) {
//如果给定的key表示的节点在n后面的话,继续往后找。
q = r;
r = r.right;
continue;
}
}
//如果r为null,那么往下找。
//获取q的下节点d
Index<K,V> d = q.down;
if (d != null) {
//如果d不为null,将d赋给q,d的右节点赋给r,再次循环。
q = d;
r = d.right;
} else
return q.node; //如果d为空,说明q就是最底层的节点,返回这个节点。
}
}
}
上面的doPut方法中,最后还有一个生成level index的部分,首先调用randomLevel得到一个level值,如果这个值大于0,就调用insertIndex生成一个index,先看下randomLevel:
/**
* Returns a random level for inserting a new node.
* Hardwired to k=1, p=0.5, max 31 (see above and
* Pugh's "Skip List Cookbook", sec 3.4).
*
* This uses the simplest of the generators described in George
* Marsaglia's "Xorshift RNGs" paper. This is not a high-quality
* generator but is acceptable here.
*/
private int randomLevel() {
int x = randomSeed;
x ^= x << 13;
x ^= x >>> 17;
randomSeed = x ^= x << 5;
if ((x & 0x8001) != 0) // test highest and lowest bits
return 0;
int level = 1;
while (((x >>>= 1) & 1) != 0) ++level;
return level;
}
继续看insertIndex方法:
/**
* 为给定的数据节点创建和添加Index节点。
*/
private void insertIndex(Node<K,V> z, int level) {
HeadIndex<K,V> h = head;
int max = h.level;
if (level <= max) {
Index<K,V> idx = null;
//如果level比当前的max level小,那么创建level个节点,纵向链接起来
//(level2的down节点指向level1、level3的down节点指向level2...)
for (int i = 1; i <= level; ++i)
idx = new Index<K,V>(z, idx, null);
//添加index。
addIndex(idx, h, level);
} else { //如果level比当前的max level大,添加level。
/*
* 为了减小其他线程在tryReduceLevel方法中检测空level的干扰,
* 新的level添加时右节点就已经初始化好了。它们被依次放到一个
* 数组里面,当创建新的head index时,会反向访问它们。
*/
//level设置为max+1
level = max + 1;
//建立一个长度为level的Index数组,
Index<K,V>[] idxs = (Index<K,V>[])new Index[level+1];
Index<K,V> idx = null;
//还是创建level个节点,纵向链接起来,同时将它们放入Index数组。
for (int i = 1; i <= level; ++i)
idxs[i] = idx = new Index<K,V>(z, idx, null);
HeadIndex<K,V> oldh;
int k;
for (;;) {
oldh = head;
int oldLevel = oldh.level;
if (level <= oldLevel) { // 竞争失败,跳出。
k = level;
break;
}
HeadIndex<K,V> newh = oldh;
Node<K,V> oldbase = oldh.node;
for (int j = oldLevel+1; j <= level; ++j)
/*
*这里创建新的HeadIndex,其数据节点为oldBase,down节点为
*之前的head,right节点为上面Index中level最高的节点,level为j
*/
newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
//尝试将head设置为新创建的HeadIndex。
if (casHead(oldh, newh)) {
k = oldLevel;
break;
}
}
//添加index。
addIndex(idxs[k], oldh, k);
}
}
再继续看下这个addIndex方法:
private void addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) {
// 记录下一个要添加的level,以防重试。
int insertionLevel = indexLevel;
Comparable<? super K> key = comparable(idx.node.key);
if (key == null) throw new NullPointerException();
// 和findPredecessor过程类似,只是在过程中会添加index节点。
for (;;) {
int j = h.level;
Index<K,V> q = h;
Index<K,V> r = q.right;
Index<K,V> t = idx;
for (;;) {
if (r != null) {
Node<K,V> n = r.node;
// compare before deletion check avoids needing recheck
int c = key.compareTo(n.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) {
// 这里还需要检查t节点是否被删除,如果t节点被删除,就不能插入。
if (t.indexesDeletedNode()) {
findNode(key); // cleans up
return;
}
//尝试将Index t插入q和q的right节点r之间。
if (!q.link(r, t))
break; // restart
if (--insertionLevel == 0) {
// 最后还要做一次删除检测。
if (t.indexesDeletedNode())
findNode(key);
return;
}
}
if (--j >= insertionLevel && j < indexLevel)
t = t.down;
q = q.down;
r = q.right;
}
}
}
这个方法要做的事情其实就是:针对一个给定的节点和level值,将之前建立的从上到下的Index节点链接进来,如图:
当然方法有几个地方要检测当前的idx节点有没有被删除,如果有,要调用一个findNode来做调整,看下这个方法:
/**
* 通过给定的key查找对应的数据节点,查找过程中会顺便清理一些
* 已经标记为删除的节点。
* 一些地方会调用这个方法,不是为了查找节点,而是为了使用清理
* 删除节点的这个"副作用"。
*
* 下列情况出现时,会重新遍历:
*
* (1) 在读取了n的next域之后,n不再是b当前的后继节点了,这意味
* 着我们没有和之前保持一致的3节点(b->n->f)快照,所以无法
* 删除后续节点。
*
* (2) 节点n的value域为null,说明n已经被删除。这种情况下,我们
* 先帮助推进n节点的删除,然后再重试。
*
* (3) n是一个标记节点或者n的前驱节点的value域为null。意味着
* findPredecessor方法会返回一个被删除的节点。我们无法移
* 除这节点,因为无法确定它的前驱节点。所以再次调用findPredecessor
* (findPredecessor方法中会处理这个情况)并返正确的前驱节点。
*/
private Node<K,V> findNode(Comparable<? super K> key) {
for (;;) {
//找到key对应的在base_level上的前驱节点。
Node<K,V> b = findPredecessor(key);
Node<K,V> n = b.next;
for (;;) {
if (n == null)
return null;
Node<K,V> f = n.next;
if (n != b.next) // 读取不一致,说明发生竞争,重试。
break;
Object v = n.value;
if (v == null) { // n被删除了,帮助推进n的删除,重试。
n.helpDelete(b, f);
break;
}
if (v == n || b.value == null) // b被删除了,重试。
break;
//开始比较
int c = key.compareTo(n.key);
if (c == 0)
return n; //找到对应节点。
if (c < 0)
return null;
b = n;
n = f;
}
}
}
- 分析完了put方法,再看下get方法:
public V get(Object key) {
return doGet(key);
}
private V doGet(Object okey) {
//转换成可比较的Key。
Comparable<? super K> key = comparable(okey);
Node<K,V> bound = null;
Index<K,V> q = head;
Index<K,V> r = q.right;
Node<K,V> n;
K k;
int c;
for (;;) {
Index<K,V> d;
// 向右遍历,一直到null或者当前给定key(对应的节点)大的节点(bound)
// 当前给定key对应的节点应该在bound的左边。
if (r != null && (n = r.node) != bound && (k = n.key) != null) {
if ((c = key.compareTo(k)) > 0) {
q = r;
r = r.right;
continue;
} else if (c == 0) {
Object v = n.value;
return (v != null)? (V)v : getUsingFindNode(key);
} else
bound = n;
}
// 往下找。
if ((d = q.down) != null) {
q = d;
r = d.right;
} else
break;
}
// 现在到了base_level,往后找就可以了。
for (n = q.node.next; n != null; n = n.next) {
if ((k = n.key) != null) {
if ((c = key.compareTo(k)) == 0) {
Object v = n.value;
return (v != null)? (V)v : getUsingFindNode(key);
} else if (c < 0)
break;
}
}
return null;
}
代码中还会看到,如果找到了节点,还会判断节点上的value是否为null。如果不为null,直接返回这个value;如果为null,说明这个节点被删除了(正在删除过程中),那么需要调用一个getUsingFindNode方法,看下这个方法:
private V getUsingFindNode(Comparable<? super K> key) {
for (;;) {
Node<K,V> n = findNode(key);
if (n == null)
return null;
Object v = n.value;
if (v != null)
return (V)v;
}
}
- 继续看下containsKey方法:
public boolean containsKey(Object key) {
return doGet(key) != null;
}
- 接着看下remove方法:
public V remove(Object key) {
return doRemove(key, null);
}
final V doRemove(Object okey, Object value) {
//转换成可比较的key
Comparable<? super K> key = comparable(okey);
for (;;) {
//找到key在base_level链上的前驱节点。
Node<K,V> b = findPredecessor(key);
Node<K,V> n = b.next;
for (;;) {
if (n == null)
return null;
Node<K,V> f = n.next;
if (n != b.next) // 读取不一致,重试。
break;
Object v = n.value;
if (v == null) { // 如果n被删除了,帮助推进删除,然后重试。
n.helpDelete(b, f);
break;
}
if (v == n || b.value == null) // 如果b被删除了,重试。
break;
int c = key.compareTo(n.key);
if (c < 0)
return null; //如果比找到的前驱节点的后继节点小,说明没有指定的key对应的节点,返回null。
if (c > 0) {
//如果比找到的前驱节点的后继节点大,说明目标节点在这个节点后面,往后找。
b = n;
n = f;
continue;
}
if (value != null && !value.equals(v))
return null;//给定的value和链表中的value不一致,删除失败。
if (!n.casValue(v, null))//首先尝试将要删除的目标节点n的value置空。
break; //如果失败,说明发生竞争,重试。
/*
* 如果上一步将n的value置空成功,接下来首先尝试将n的后面追加一个标记节点,
* 成功的话,再尝试将n和标记节点一起移除,这两部有任何一步失败,都会调用
* findNode来完成删除(利用findNode方法的副作用)
*/
if (!n.appendMarker(f) || !b.casNext(n, f))
findNode(key); // Retry via findNode
else {
/*
* 如果上面的n.appendMarker(f)和b.casNext(n, f)都调用成功,
* 然后就会调用这个方法,注意这里其实也是使用这个方法的
* 副作用来删除节点n的Index节点。
*/
findPredecessor(key);
if (head.right == null)
// 删除了一些Index之后,这里判断一下head的right节点是否为null,
// 如果为null,说明最高层的Index链已经不存在数据,可以删掉了。
tryReduceLevel();
}
return (V)v;
}
}
}
看一下上面方法中最后调用的tryReduceLevel方法:
private void tryReduceLevel() {
HeadIndex<K,V> h = head;
HeadIndex<K,V> d;
HeadIndex<K,V> e;
if (h.level > 3 &&
(d = (HeadIndex<K,V>)h.down) != null &&
(e = (HeadIndex<K,V>)d.down) != null &&
e.right == null &&
d.right == null &&
h.right == null &&
casHead(h, d) && // try to set
h.right != null) // recheck
casHead(d, h); // try to backout
}
- 再看下containsValue方法:
public boolean containsValue(Object value) {
if (value == null)
throw new NullPointerException();
for (Node<K,V> n = findFirst(); n != null; n = n.next) {
V v = n.getValidValue();
if (v != null && value.equals(v))
return true;
}
return false;
}
看下上面用到的findFirst方法:
Node<K,V> findFirst() {
for (;;) {
Node<K,V> b = head.node;
Node<K,V> n = b.next;
if (n == null)
return null;
if (n.value != null)
return n;
n.helpDelete(b, n.next);
}
}
就是找到head中node(BASE_HEADER节点)的next,有可能next节点被删除了,所以会做检测,删除的话,推进一下删除,然后继续获取。size和isEmpty也是基于这个方法实现的:
public int size() {
long count = 0;
for (Node<K,V> n = findFirst(); n != null; n = n.next) {
if (n.getValidValue() != null)
++count;
}
return (count >= Integer.MAX_VALUE)? Integer.MAX_VALUE : (int)count;
}
public boolean isEmpty() {
return findFirst() == null;
}
- ConcurrentSkipListMap实现了ConcurrentMap接口,看下这些接口方法的实现:
public V putIfAbsent(K key, V value) {
if (value == null)
throw new NullPointerException();
return doPut(key, value, true);
}
public boolean remove(Object key, Object value) {
if (key == null)
throw new NullPointerException();
if (value == null)
return false;
return doRemove(key, value) != null;
}
public boolean replace(K key, V oldValue, V newValue) {
if (oldValue == null || newValue == null)
throw new NullPointerException();
Comparable<? super K> k = comparable(key);
for (;;) {
Node<K,V> n = findNode(k);
if (n == null)
return false;
Object v = n.value;
if (v != null) {
if (!oldValue.equals(v))
return false;
if (n.casValue(v, newValue))
return true;
}
}
}
public V replace(K key, V value) {
if (value == null)
throw new NullPointerException();
Comparable<? super K> k = comparable(key);
for (;;) {
Node<K,V> n = findNode(k);
if (n == null)
return null;
Object v = n.value;
if (v != null && n.casValue(v, value))
return (V)v;
}
}
- ConcurrentSkipListMap同样实现了SortedMap接口,看下相关接口方法的实现:
public K lastKey() {
Node<K,V> n = findLast();
if (n == null)
throw new NoSuchElementException();
return n.key;
}
这里出现了一个findLast方法,之前没分析过,看下:
Node<K,V> findLast() {
Index<K,V> q = head;
for (;;) {
Index<K,V> d, r;
if ((r = q.right) != null) {
if (r.indexesDeletedNode()) { //如果发现节点已经删除的Index,顺便移除。
q.unlink(r);
q = head; // 重试。
}
else
q = r; //向右找
} else if ((d = q.down) != null) {
q = d; //向下找
} else {
//现在到了base_level链上,向后找。
Node<K,V> b = q.node;
Node<K,V> n = b.next;
for (;;) {
if (n == null)
//最后定位到节点后需要检测一下是不是baseHead
return (b.isBaseHeader())? null : b;
Node<K,V> f = n.next; // 读取不一致,有竞争发生,重试。
if (n != b.next)
break;
Object v = n.value;
if (v == null) { // n节点被删除,重试。
n.helpDelete(b, f);
break;
}
if (v == n || b.value == null) // b节点被删除,重试。
break;
b = n;
n = f;
}
q = head; // restart
}
}
}
public Map.Entry<K,V> lowerEntry(K key) {
return getNear(key, LT);
}
这个方法的意思是找到一个比给定key小的所有key里面最大的key对应的Entry,里面调用了getNear方法:
AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {
for (;;) {
Node<K,V> n = findNear(key, rel);
if (n == null)
return null;
AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();
if (e != null)
return e;
}
}
getNear方法里面首先通过findNear方法找到指定的Node,然后通过createSnapshot方法返回一个Entry,这个方法最开始的时候看到过。下面重点看下这个findNear方法:
private static final int EQ = 1;
private static final int LT = 2;
private static final int GT = 0; // Actually checked as !LT
/**
* Utility for ceiling, floor, lower, higher methods.
* @param kkey the key
* @param rel the relation -- OR'ed combination of EQ, LT, GT
* @return nearest node fitting relation, or null if no such
*/
Node<K,V> findNear(K kkey, int rel) {
Comparable<? super K> key = comparable(kkey);
for (;;) {
Node<K,V> b = findPredecessor(key);
Node<K,V> n = b.next;
for (;;) {
if (n == null)
return ((rel & LT) == 0 || b.isBaseHeader())? null : b; //出口1
Node<K,V> f = n.next;
if (n != b.next) // inconsistent read
break;
Object v = n.value;
if (v == null) { // n is deleted
n.helpDelete(b, f);
break;
}
if (v == n || b.value == null) // b is deleted
break;
int c = key.compareTo(n.key);
if ((c == 0 && (rel & EQ) != 0) ||
(c < 0 && (rel & LT) == 0))
return n; //出口2
if ( c <= 0 && (rel & LT) != 0)
return (b.isBaseHeader())? null : b; //出口3
b = n;
n = f;
}
}
}
public Map.Entry<K,V> floorEntry(K key) {
return getNear(key, LT|EQ);
}
public Map.Entry<K,V> ceilingEntry(K key) {
return getNear(key, GT|EQ);
}
public Map.Entry<K,V> higherEntry(K key) {
return getNear(key, GT);
}
- ConcurrentSkipListMap分析到这里,一些关键的地方已经分析到了,至于其他没覆盖到的方法,基本都是基于上面分析的方法或思路来实现的,这里就不一一分析了。
- 最后,ConcurrentSkipListSet基于ConcurrentSkipListMap实现的:
public class ConcurrentSkipListSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable {
private static final long serialVersionUID = -2479143111061671589L;
private final ConcurrentNavigableMap<E,Object> m;
public ConcurrentSkipListSet() {
m = new ConcurrentSkipListMap<E,Object>();
}
...
public boolean add(E e) {
return m.putIfAbsent(e, Boolean.TRUE) == null;
}
...
ok,代码解析完毕!