1. 简介
ConcurrentSkipListMap是有序的hash表,是线程安全的。
与之对比的另外两种hash容器,ConcurrentHashMap虽然是线程安全的,但是key并不是有序的;而TreeMap虽然key是有序的,但是不是线程安全的。
ConcurrentSkipListMap采用无锁方案,支持更高的并发,存取时间是O(logN),与线程数无关。也就是说,当数据量一定的情况下,并发线程数越多,ConcurrentSkipListMap优势越大。
2. 数据结构
ConcurrentSkipListMap是一种并发的、基于跳表的Map。
2.1 属性
(1)Node节点,其中的value域和next域皆采用volatile来实现并发,而key是不可变的,因此用final修饰。
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;
}
...
}
(2)index节点,就是跳表中的索引节点,其中包含了Node节点、指向下边index节点的指针、指向右边index节点的指针。
static class Index<K,V> {
final Node<K,V> node;
final Index<K,V> down;
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;
}
...
}
(3)HeadIndex节点是链表的头部索引节点,包含一个level域,表示该跳表总共有几层索引。并且其中不存储有意义的数据。
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;
}
}
(4)用于标记底层Head的value
private static final Object BASE_HEADER = new Object();
(5)跳表最顶层的Head
private transient volatile HeadIndex<K,V> head;
(6)用于给key排序的comparator
final Comparator<? super K> comparator;
2.2 构造方法
(1)无参构造器
public ConcurrentSkipListMap() {
//自动排序
this.comparator = null;
initialize();
}
private void initialize() {
keySet = null;
entrySet = null;
values = null;
descendingMap = null;
//创建顶层的head对象,将用于标记底层的BASE_HEADER赋值给该head的Node的value域
head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null),
null, null, 1);
}
(2)构造器中还可以手动传入comparator,用于自定义排序
public ConcurrentSkipListMap(Comparator<? super K> comparator) {
this.comparator = comparator;
initialize();
}
2.3 添加节点
主要使用put方法来添加键值对。注意:ConcurrentSkipListMap中的key和value都不能为null。
public V put(K key, V value) {
//value不允许为null
if (value == null)
throw new NullPointerException();
return doPut(key, value, false);
}
private V doPut(K key, V value, boolean onlyIfAbsent) {
//z将指向要添加的节点
Node<K,V> z;
//key不允许为null
if (key == null)
throw new NullPointerExcepti