【源码学习】之ConcurrentSkipListMap

ConcurrentSkipListMap的原理如下:
在跳表中,基本的数据结构变为HeadIndex,有right、down两个指针,有node属性,node属性中记录的是节点的信息并指向下一个节点。通过volatile实现了并发操作。这里,附带介绍一下volatile的原理和实际使用场景

static final class Node<K,V> {
    
    
	final K key;
	volatile Object value;
	volatile Node<K,V> next;
}
static class Index<K,V> {
    
    
    final Node<K,V> node;
    final Index<K,V> down;//downy引用
    volatile Index<K,V> righ                      
    ……
}

ConcurrentSkipListMap的put步骤:

  1. 找到key的前继节点b,n指向b的后继节点
  2. 重新获取b的后继节点,如果不是n,则证明其他线程修改了,需要重新找前继节点和后继节点
  3. 如果b的后继节点就是n,那么获取n节点的value,如果value是null,说明这个节点已经被删除了,再次重新找前继节点和后继节点。
  4. 如果b被删除了,则重新找前继节点和后继节点。
  5. 如果都没有上面的问题,则新建节点,将新节点插入跳表中,过程中用到了casNext函数,函数的解释是:compareAndSet next field,内部使用了sun.misc.Unsafe的compareAndSwapObject方法,是进行原子操作的,具体的解释请看这里
  6. 插入节点(insertIndex)的过程,首先要用随机算法产生一个最高level,如果这个最高level小于等于现在已有的最高level,那么就对从1到最高level都增加addIndex,否则,要先创建level,然后再执行addIndex。
  7. addIndex是指插入节点,首先会从头节点开始,一层一层往下遍历,如果当前层等于要插入节点的最高层,则需要执行link操作,link也是原子操作,将该节点插入本层合适的位置。在这个过程中,我们都解决的是每一层的right指针指到哪个节点的问题,那么down指针呢?是不是太迷茫了,down指针其实在创建之初就指向了下一个节点。
idx = new Index<K,V>(z, idx, null);  
  1. 如果要创建新的层数,则需要新建一个HeadIndex,casHead函数保证了操作的原子性,之后再进行插入节点操作。
// 新建节点(对应是“要插入的键值对”)
Node<K,V> z = new Node<K,V>(kkey, value, n);
// 设置“b的后继节点”为z
if (!b.casNext(n, z))
    break;         // 多线程情况下,break才可能发生(其它线程对b进行了操作)
int level = randomLevel();//得到一个随机的level作为该key-value插入的最高level
if (level > 0)
    insertIndex(z, level);//进行插入操作
return null;
/**
* casNext函数内部
* compareAndSet next field
*/                
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
    
    
	// 比较和交换
	return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
/**
* insertIndex 函数主要步骤
*/
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;
	for (int i = 1; i <= level; ++i)
		idx = new Index<K,V>(z, idx, null);
	addIndex(idx, h, level);
	} else {
    
    
		for (int j = oldLevel+1; j <= level; ++j)
			newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);
		if (casHead(oldh, newh)) {
    
    
			k = oldLevel;
			break;
		}
		addIndex(idxs[k], oldh, k);
	}
1. Unsafe
1.Unsafe是一个静态类,单例模式,不能使用构造函数创建对象
2.可用于管理和操作内存。
3.可以进行CAS原子操作
4.可以进行线程操作
2. CAS
 jvm中的CAS操作是基于处理器的CMPXCHG指令实现的
 CAS有三个操作数:内存值V、旧的预期值A、要修改的值B。,
 当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做并返回false
节点的最高层是由随机数决定的,Xorshift RNGs生成随机数的方法,采用异或和位移,每次产生不同的32位,周期为2^32-1

ConcurrentSkipListMap的get步骤:

  1. 从最上层的头节点开始,遍历right节点,如果找到了相等的key节点,则返回这个节点的value
  2. 如果没有找到,则到期down指向的节点查找
  3. 如果已经到了最后一层,还是没有找到,则用next的方式进行查找

删除也是从上往下,一层一层删除的
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u010659877/article/details/108768512