目录
6、remove / replace / replaceAll / clear
ConcurrentSkipListSet是基于ConcurrentSkipListMap实现的线程安全的NavigableSet接口实现类,与之对应的线程不安全的实现类就是基于TreeMap实现的TreeSet;ConcurrentNavigableMap是基于SkipList跳跃表实现的线程安全的NavigableMap实现类,后者对应的线程不安全的实现类就是基于红黑树实现的TreeMap。本篇博客就详细探讨这两类的实现细节。
一、ConcurrentSkipListSet
ConcurrentSkipListSet的类继承关系如下:
其实现的核心接口NavigableSet的另一个典型实现类就是TreeSet,跟TreeSet基于TreeMap实现一样, ConcurrentSkipListSet的实现基于ConcurrentSkipListMap的,添加和删除元素,元素遍历等都是基于ConcurrentSkipListMap方法的,value固定为Boolean.TRUE,其常用方法实现如下:
public ConcurrentSkipListSet() {
m = new ConcurrentSkipListMap<E,Object>();
}
public ConcurrentSkipListSet(Comparator<? super E> comparator) {
m = new ConcurrentSkipListMap<E,Object>(comparator);
}
public boolean add(E e) {
return m.putIfAbsent(e, Boolean.TRUE) == null;
}
public boolean remove(Object o) {
return m.remove(o, Boolean.TRUE);
}
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
public Iterator<E> descendingIterator() {
return m.descendingKeySet().iterator();
}
二、ConcurrentSkipListMap
1、定义
ConcurrentSkipListMap的类继承关系如下:
从上图可知,ConcurrentNavigableMap继承自ConcurrentMap和NavigableMap,后者的典型实现类就是TreeMap,纯粹是基于红黑树实现的,可以参考《java8 TreeMap接口实现源码解析》。ConcurrentNavigableMap没有添加新的方法,只是改写了父类接口的方法定义,如下:
这些方法的用途可以参考API,会挑选其中典型的方法研究其实现细节。该类包含的属性如下:
//头索引节点
private transient volatile HeadIndex<K,V> head;
//比较大小的比较器
final Comparator<? super K> comparator;
//key视图,下面这些视图都是在请求相关方法时才会创建
private transient KeySet<K> keySet;
//键值对视图
private transient EntrySet<K,V> entrySet;
//value视图
private transient Values<V> values;
//倒序遍历的视图
private transient ConcurrentNavigableMap<K,V> descendingMap;
包含的静态常量如下:
//初始化head属性时使用,表示一个空节点
private static final Object BASE_HEADER = new Object();
private static final int EQ = 1;
private static final int LT = 2;
private static final int GT = 0; // Actually checked as !LT
包含的静态属性通过static代码块初始化,如下:
2、Node / Index / HeadIndex
这三个都是内部类,其中Node表示一个键值对,其定义如下:
static final class Node<K,V> {
//键值对的key和value
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;
}
Node(Node<K,V> next) {
this.key = null;
this.value = this;
this.next = next;
}
//cas修改value
boolean casValue(Object cmp, Object val) {
return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val);
}
//cas修改next
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
boolean isMarker() {
return value == this;
}
boolean isBaseHeader() {
return value == BASE_HEADER;
}
//插入一个value指向自己的节点,next为f的节点
//删除某个节点时会将其value置为null,然后调用此方法在该节点后插入一个marker,将该节点从next链表中移除
//marker在这里是打一个标识同时保存下一个节点的引用,保证将其从next链表中移除失败,即cas修改前一个节点的next属性失败的情形下该节点可以被正常移除
boolean appendMarker(Node<K,V> f) {
return casNext(f, new Node<K,V>(f));
}
//在查询或者修改节点时,发现某个节点的value已经是null了,会调用此方法
//b是前一个节点,f是next节点
void helpDelete(Node<K,V> b, Node<K,V> f) {
if (f == next && this == b.next) {
//再次检查链表关系
if (f == null || f.value != f)
//该节点未插入marker节点,此处重新插入,下次调用时进入else分支
casNext(f, new Node<K,V>(f));
else
//f是一个marker节点
//将this和f都从链表中移除
b.casNext(this, f.next);
}
}
V getValidValue() {
Object v = value;
if (v == this || v == BASE_HEADER) //value是无效
return null;
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() {
Object v = value;
if (v == null || v == this || v == BASE_HEADER) //value是无效
return null;
@SuppressWarnings("unchecked") V vv = (V)v;
return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
}
//获取属性偏移量
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = Node.class;
valueOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("value"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
Index是HeadIndex的父类,都表示SkipList中的索引节点,其实现如下:
static class Index<K,V> {
//关联的node节点
final Node<K,V> node;
//down指向的节点的node和当前节点的node是同一个,但是down节点位于下一个层级,层级越往下包含的节点数越多,最底层包含了所有的node节点
final Index<K,V> down;
//right指向的节点的key大于当前节点,跟当前节点位于同一个层级,沿着right属性构成的链表遍历,node的key值越来越大
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;
}
//cas修改right属性
final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val);
}
final boolean indexesDeletedNode() {
return node.value == null;
}
//将新节点插入到当前节点的rigth链表中,原right节点的前面
final boolean link(Index<K,V> succ, Index<K,V> newSucc) {
Node<K,V> n = node;
newSucc.right = succ;
//node的vlue不为空时,将right由succ修改成newSucc
return n.value != null && casRight(succ, newSucc);
}
//将succ从right链表中移除
final boolean unlink(Index<K,V> succ) {
//node的vlue不为空时,将right由succ修改成succ.right
return node.value != null && casRight(succ, succ.right);
}
//获取right属性偏移量
private static final sun.misc.Unsafe UNSAFE;
private static final long rightOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = Index.class;
rightOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("right"));
} catch (Exception e) {
throw new Error(e);
}
}
}
static final class HeadIndex<K,V> extends Index<K,V> {
//level记录层级
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;
}
}
3、SkipList 跳跃表
跳跃表是一种改进自链表的数据结构,用于存储有序的数据,通过空间换时间的方法来提高数据检索的速度。 普通的链表或者数组插入和删除的效率都是O(1),但是查找的效率是O(n),如果是有序链表或者有序数组,则可以通过二分法快速查找,复杂度是O(logn)。跳跃表在理想情况下是O(logn),最差的情况下是O(n),因为跳跃表“跳跃”的下一个节点是完全随机的。其数据结构如下图:
如图所示,跳跃表是有多个层级的,每个层级都是一个有序的链表,越往上的层级节点数越少,最底层则包含了所有的节点,每一层的节点的起始节点都是head节点。
如图所示,跳跃表的节点有两种,一种是最底层的,层级是0的节点,跟普通链表的节点一样,通过next属性构成链表,就是上面的Node类;一种是索引节点,就是上面的Index类,索引节点包含对Node的引用,除此之外还有两个属性,down属性保存下一层级节点的引用,即图中向下的箭头引用,right属性保存同一层级中比当前节点指向的Node大的的节点,即图中向右的箭头引用,注意通过down属性链接起来的节点其指向的Node都是同一个且这些节点都是在Node节点创建时根据Node节点的层级一起创建的。还有一种特殊的索引节点,图中的head节点,会额外保存当前跳跃表的层级,即HeadIndex类中的level属性,在插入新节点时会判断新节点的层级是否大于当前head节点的层级,如果是,需要相应的增加一个head节点,另外,元素遍历时也是通过左上角的head节点开始遍历的,先往右查找,如果碰到比自己大的节点,则在下一层级继续往右查找,直到找到最底层的节点,再通过next属性遍历,找到匹配的节点。跳跃的实现相比普通链表增加了索引节点,通过索引节点跳过一些中间节点,从而提高查找的效率,这就是所谓的空间换时间了。
注意,跳跃表的层级是取决于所有节点的最大层级的,即如果某个节点的层级大于当前跳跃表的层级,会将当前跳跃表的层级加1,同时创建一个新的head节点,新head节点的right属性指向该节点。而新节点所属的层级是随机的,在插入时通过随机算法确定的,50%的概率是0,25%的概率是1,12.5%的概率是2,层级越高的概率越低。
理解上述逻辑,可以通过如下测试用例Debug:
@Test
public void test() throws Exception {
Random random=new Random();
Map<Integer,Integer> test=new ConcurrentSkipListMap<>();
for(int i=0;i<90;i++){
test.put(random.nextInt(90),1);
}
test.put(random.nextInt(100),1);
test.put(random.nextInt(100),1);
test.put(random.nextInt(100),1);
test.put(random.nextInt(100),1);
for(int key:test.keySet()){
System.out.println(key);
}
}
在后面的test.put处打断点,然后进入到doPut方法调试,前面插入的90个节点保证跳跃表已经形成了多个层级; 如果想要查看层级非0的节点的插入逻辑,则直接在doPut方法内已经算出随机数的地方打断点。下图是进入doPut方法后,打印的head节点:
从图中可知,通过down属性链接的多个节点指向同一个Node节点。
4、构造方法
public ConcurrentSkipListMap() {
this.comparator = null;
initialize();
}
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
this.comparator = comparator;
initialize();
}
public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {
this.comparator = null;
initialize();
//使用父类AbstractMap的实现,遍历m中的键值对,通过put方法添加到当前map中
putAll(m);
}
public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {
this.comparator = m.comparator();
initialize();
buildFromSorted(m);
}
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);
}
//该方法是在构造方法中调用的,不会被并行访问,所以构建跳跃表的过程中没有使用CAS
private void buildFromSorted(SortedMap<K, ? extends V> map) {
if (map == null)
throw new NullPointerException();
HeadIndex<K,V> h = head;
Node<K,V> basepred = h.node;
ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();
//初始化数组元素的
for (int i = 0; i <= h.level; ++i)
preds.add(null);
Index<K,V> q = h;
//将每一层的head节点保存到preds中,下面的while循环过程中会把同一层级的最右侧的节点保存到preds中
for (int i = h.level; i > 0; --i) {
preds.set(i, q);
q = q.down;
}
Iterator<? extends Map.Entry<? extends K, ? extends V>> it =
map.entrySet().iterator();
//因为SortedMap中的节点是已经排序好了,所以这里直接构建跳跃表即可,不需要为新节点查找插入位置
while (it.hasNext()) {
Map.Entry<? extends K, ? extends V> e = it.next();
//生成一个随机数
int rnd = ThreadLocalRandom.current().nextInt();
int j = 0;
//计算新节点的层级
if ((rnd & 0x80000001) == 0) { //如果rnd是一个偶数
do {
++j;
} while (((rnd >>>= 1) & 1) != 0); //将rnd右移1位,即除以2,如果结果是偶数就终止循环,否则继续
//如果大于head的level
if (j > h.level) j = h.level + 1;
}
K k = e.getKey();
V v = e.getValue();
if (k == null || v == null)
throw new NullPointerException();
Node<K,V> z = new Node<K,V>(k, v, null);
basepred.next = z;
basepred = z;
if (j > 0) {
Index<K,V> idx = null;
//从层级1开始往上逐一处理
for (int i = 1; i <= j; ++i) {
idx = new Index<K,V>(z, idx, null);
if (i > h.level)
//创建一个新的head节点
h = new HeadIndex<K,V>(h.node, h, idx, i);
if (i < preds.size()) {
preds.get(i).right = idx; //index作为上一个节点的right节点
preds.set(i, idx); //更新i,即当前节点是这一层级中的最右边的节点
} else
//i>h.level的情形
preds.add(idx);
}
}
}
//更新head
head = h;
}
5、put / putIfAbsent
上述两个方法的实现都是doPut方法,该方法的实现整体上可以分为两步,第一步是从最高层级的head索引节点开始往右查找,如果碰到一个比自己大的节点,则进入下一个层级继续往右查找,直到该节点是最底层的索引节点了为止,再根据该索引节点指向的链表节点,通过next属性往后遍历找到一个合适的位置,当前节点大于前面的节点小于后面的节点,然后插入到next链表中。第二步是计算当前节点所属的层级,如果层级不是0,则构建每一层级对应的索引节点,各索引节点通过down属性链接起来且都指向新创建的链表节点,然后从高层级往下遍历,将各索引节点同同一层级的节点建立关联关系,这期间如果新节点的层级高于head节点的层级,需要将当前head节点的层级加1,并创建一个对应的head节点,新的head节点的right属性指向该新节点对应的同一层级的索引节点。
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
return doPut(key, value, false);
}
public V putIfAbsent(K key, V value) {
if (value == null)
throw new NullPointerException();
return doPut(key, value, true);
}
private V doPut(K key, V value, boolean onlyIfAbsent) {
Node<K,V> z; // added node
if (key == null)
throw new NullPointerException();
Comparator<? super K> cmp = comparator;
//在next链表中找到合适的位置,将新节点插入进去
outer: for (;;) {
//findPredecessor方法返回当前Map中小于key的最大的一个索引节点所指向的节点,且该索引节点位于最底层了,然后遍历通过next属性构成的链表
//直到找到一个大于key的节点,即下面c小于0的情形,将新节点插入到该节点的前面
for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {
if (n != null) {
Object v; int c;
Node<K,V> f = n.next;
if (n != b.next) //next节点变了,终止内层for循环,通过外层的for循环重新获取b和n
break;
if ((v = n.value) == null) {//n被删除了
n.helpDelete(b, f);
break;
}
if (b.value == null || v == n) //b被删除了,下一次findPredecessor会将b从链表中移除
break;
if ((c = cpr(cmp, key, n.key)) > 0) { //如果key大于n.key,继续遍历通过next属性构成的链表中的下一个节点
b = n;
n = f;
continue;
}
if (c == 0) {
//如果找到key相等的节点,
if (onlyIfAbsent || n.casValue(v, value)) {
//onlyIfAbsent为true表示不需要修改,为false则修改value,返回原来的value
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
break; //如果cas修改失败,则继续for循环尝试修改,直到修改成功为止
}
//c小于0会进入下面的new Node
}
//如果n为null或者c小于0,c小于0时,b.key < key <n.key
//创建一个新节点,插入到b和n的中间
z = new Node<K,V>(key, value, n);
if (!b.casNext(n, z))
break; //如果修改失败则重试
break outer; //终止外层的for循环
} //内层for循环
}//外层for循环
int rnd = ThreadLocalRandom.nextSecondarySeed();
if ((rnd & 0x80000001) == 0) { //如果是偶数
//level表示新节点的层级,从1开始增加,0表示最底层的一个层架
int level = 1, max;
while (((rnd >>>= 1) & 1) != 0) //将rnd除以2以后,如果是奇数level加1
++level;
//idx表示当前节点通过down属性构成的一个链表,所有节点都指向新节点z
Index<K,V> idx = null;
HeadIndex<K,V> h = head;
if (level <= (max = h.level)) { //如果level小于head的level,则创建一个通过down属性构成的Index链表
for (int i = 1; i <= level; ++i)
//idx是作为新节点的down节点的
idx = new Index<K,V>(z, idx, null);
}
else { //如果level大于head的level,需要增加head节点的层级
level = max + 1; //比head大的话,只能在原来的基础上加1
//创建一个Index数组
@SuppressWarnings("unchecked")Index<K,V>[] idxs =
(Index<K,V>[])new Index<?,?>[level+1]; //加1是为了加上底层0对应的Index
//初始化数组元素
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) //初始状态下level肯定大于oldLevel
break;
HeadIndex<K,V> newh = h;
Node<K,V> oldbase = h.node;
//创建新层级对应的head节点,down节点指向原来的head,right节点指向新节点z对应的索引节点
//通常oldLevel就比level小1,即只创建一个HeadIndex节课
for (int j = oldLevel+1; j <= level; ++j)
newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
if (casHead(h, newh)) { //cas修改head,如果修改失败说明其他线程在修改head,则重新for循环读取head
h = newh; //使用最新的head
idx = idxs[level = oldLevel]; //此处level等于oldLevel
break;
}
}
}
//如果新节点z对应的level不是0,则创建对应层级的down链表,如果层级大于head的还需要调整head的层级,创建新的head节点,新层级的head节点的right节点
//就指向新节点z
// find insertion points and splice in
splice: for (int insertionLevel = 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) {
Node<K,V> n = r.node;
// compare before deletion check avoids needing recheck
int c = cpr(cmp, key, n.key);
if (n.value == null) { //r节点是无效的
if (!q.unlink(r)) //将r从q的right链表中移除,移除失败重新读取q
break;
r = q.right;//获取新的right节点
continue;
}
if (c > 0) {//key大于n.key,则继续遍历下一个right
q = r;
r = r.right;
continue;
}
}
//如果c小于等于0,即key小于等于n.key,或者r等于null
//初始状态j肯定是大于insertionLevel,后面没for循环一次,j减1,j就会等于insertionLevel
if (j == insertionLevel) {
if (!q.link(r, t)) //将t插入到q的right链表中,插入到r的前面
break; //link失败,重新读取h
//link成功
if (t.node.value == null) { //正常不可能为null,极端并发下可能为null,因为此时已经将新节点插入到链表中了,有可能被其他某个线程给删除了
findNode(key); //通过findNode将该节点移除,然后终止外层for循环
break splice;
}
//如果相等了,j和insertionLevel都会减1,后面的for循环中两个都会相等,如果不等则只是j减1
if (--insertionLevel == 0) //到底层节点了,终止外层for循环
break splice;
}
if (--j >= insertionLevel //j肯定大于等于insertionLevel
&& j < level) //如果j大于level,说明未遍历到新节点所在的最高层级,小于level了说明上一个层级的right节点处理好了,需要遍历下一个层级
t = t.down;
//遍历下一个层级的节点
q = q.down;
r = q.right;
} //内层for循环
}//外层for循环
}//if结束
return null;
}
//从head节点开始遍历,找到小于key的最大的一个索引节点指向的节点
private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {
if (key == null)
throw new NullPointerException(); // don't postpone errors
for (;;) {
//从head开始往后遍历
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) { //r节点即将被删除
//将r从q的right链表中移除,如果q的node的value为null或者cas失败都会则返回false
if (!q.unlink(r))
break; //终止内层for循环,通过外层的for循环重新开始,重新读取head
r = q.right; //如果unlink成功,重新读取r
continue;
}
//n.value不为null
if (cpr(cmp, key, k) > 0) {
//如果key大于k,继续遍历下一个right
q = r;
r = r.right;
continue;
}
}
//如果key小于k 或者r等于null
//如果down节点为null,说明已经到最下面的一层节点了,如果不为null,则继续遍历下一层节点
//最简单的一个场景,head只有一个层级,即down为null,right不为null,会沿着right属性构成的链表一直遍历直到遇到一个大于key的节点
//此时q就是小于key的最大的一个节点
//如果down不为null,则遍历down节点的right,注意down节点和q节点指向的node是同一个,同样的沿着right属性构成的链表一直遍历直到遇到
//一个大于key的节点,q就是上一个小于key的最大的节点
if ((d = q.down) == null)
return q.node;
//如果down不为null,则继续用down的right节点跟key比
q = d;
r = d.right;
}
}
}
//c不为空null,通过c比较大小,否则强转成Comparable比较
static final int cpr(Comparator c, Object x, Object y) {
return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y);
}
private Node<K,V> findNode(Object key) {
if (key == null)
throw new NullPointerException(); // don't postpone errors
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) //没有匹配的节点,终止外层循环,返回null
break outer;
Node<K,V> f = n.next;
if (n != b.next) //重新读取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)//找到目标节点
return n;
if (c < 0) //没有匹配节点,终止外层循环,返回null
break outer;
//c大于0,继续遍历下一个节点
b = n;
n = f;
}
}
return null;
}
private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
}
6、remove / replace / replaceAll / clear
remove方法的核心实现是doRemove,先按照同样的方式遍历找到目标节点,然后将该节点的value置为null,紧接着在该节点后插入一个空的marker节点,如果成功了则将该节点和之前插入对的marker节点一起从链表中移除,插入marker节点的原因就是为了保证后面的从链表移除的动作可以顺利完成,最后再移除该节点对应的索引节点,将其从同一层级的前一个节点的right链表中移除。 最后两步的移除动作,也可以并行的被其他线程执行,他们判断所引用的Node节点的value为null,会自动执行对应的删除动作,所以删除动作都是CAS实现的。
public V remove(Object key) {
return doRemove(key, null);
}
public boolean remove(Object key, Object value) {
if (key == null)
throw new NullPointerException();
return value != null && doRemove(key, value) != null;
}
//将等于key的节点的value替换成指定值,返回替换前的值,相当于put方法
public V replace(K key, V value) {
if (key == null || value == null)
throw new NullPointerException();
for (;;) {
Node<K,V> n; Object v;
if ((n = findNode(key)) == null)
return null; //如果没有找到目标节点
//校验n.value不为空是为了避免该节点被删除了
//然后cas修改value
if ((v = n.value) != null && n.casValue(v, value)) {
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;//返回以前的值
}
}
}
//要求key和value都相等才替换,返回替换是否成功
public boolean replace(K key, V oldValue, V newValue) {
if (key == null || oldValue == null || newValue == null)
throw new NullPointerException();
for (;;) {
Node<K,V> n; Object v;
if ((n = findNode(key)) == null)
return false;
if ((v = n.value) != null) {
if (!oldValue.equals(v)) //如果当前值不是oldValue返回false
return false;
if (n.casValue(v, newValue)) //如果cas修改成功,返回true
return true;
}
}
}
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
if (function == null) throw new NullPointerException();
V v;
//获取第一个有效节点后,通过next遍历所有的节点
for (Node<K,V> n = findFirst(); n != null; n = n.next) {
while ((v = n.getValidValue()) != null) { //获取有效value
V r = function.apply(n.key, v);
//返回值为空,抛出异常
if (r == null) throw new NullPointerException();
if (n.casValue(v, r)) //cas修改value成功则终止内层while循环,处理下一个节点
break;
}
}
}
//获取第一个有效节点,其实就是head.node.next
final Node<K,V> findFirst() {
for (Node<K,V> b, n;;) {
if ((n = (b = head.node).next) == null)
return null; //如果next为null,返回null
if (n.value != null)
return n;
//如果n.value为null,则删除该节点
n.helpDelete(b, n.next);
}
}
final V doRemove(Object key, Object value) {
if (key == null)
throw new NullPointerException();
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) //没有匹配的节点,终止外层for循环,返回null
break outer;
Node<K,V> f = n.next;
if (n != b.next) //next节点被修改了,重新读取b
break;
if ((v = n.value) == null) { //n被删除了
n.helpDelete(b, f); //删除节点n
break;
}
if (b.value == null || v == n) // b被删除了,重新读取b
break;
if ((c = cpr(cmp, key, n.key)) < 0) //key小于n.key,不会有匹配的节点了
break outer;
if (c > 0) { //key大于n.key,继续遍历下一个next节点
b = n;
n = f;
continue;
}
//c等于0
if (value != null && !value.equals(v)) //value不为空跟当前节点的value不一致,终止外层for循环,返回null
break outer;
if (!n.casValue(v, null)) //cas修改失败,内层for循环重试
break;
//casValue成功,在n和f之间插入一个空节点,如果成功则将b的next节点修改成f
if (!n.appendMarker(f) || !b.casNext(n, f))
//value已经置为null,其他线程不可能修改n的next属性,此时只有修改b的next属性会失败
findNode(key); //上述操作有一个失败则执行findNode,借此清除节点和关联的索引节点
else {
//修改成功,通过findPredecessor清理关联的Index节点
findPredecessor(key, cmp); // clean index
if (head.right == null) //如果head的right节点为null,则尝试减少层级
tryReduceLevel();
}
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
}
return null;
}
public void clear() {
for (;;) {
Node<K,V> b, n;
HeadIndex<K,V> h = head, d = (HeadIndex<K,V>)h.down;
if (d != null)
casHead(h, d); //修改head,原head的right链表就都被移除了
//d为null,遍历到最底层的节点了
else if ((b = h.node) != null && (n = b.next) != null) {
Node<K,V> f = n.next; // remove values
if (n == b.next) { //b未改变
Object v = n.value;
if (v == null) //n被删除了
n.helpDelete(b, f);
//v不为null,将n的value置为null并插入marker,将b的next由n修改成f
else if (n.casValue(v, null) && n.appendMarker(f))
b.casNext(n, f); //如果cas成功,则n从链表中移除了,如果失败了,假如此时其他线程在并行的往b后面插入一个节点,则下次遍历到n时通过helpDelete将其从链表中移除
}
}
else
break;
}
}
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 && //从head往下连续三个层级的right都为null
casHead(h, d) && //将head从h修改成d
h.right != null) // 最后一次检查,如果不为null则恢复
casHead(d, h); // try to backout
}
7、get / getOrDefault
其底层实现doGet方法的实现和findNode方法是基本一致的,如下:
public V get(Object key) {
return doGet(key);
}
public V getOrDefault(Object key, V defaultValue) {
V v;
return (v = doGet(key)) == null ? defaultValue : v;
}
//doGet方法的实现和findNode方法是基本一致的
private V doGet(Object key) {
if (key == null)
throw new NullPointerException();
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) //重新读取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) { //找到目标节点
@SuppressWarnings("unchecked") V vv = (V)v;
return vv;
}
if (c < 0)
break outer; 没有匹配节点
//c大于0,则遍历下一个next节点
b = n;
n = f;
}
}
return null;
}