Java多线程系列--“JUC集合”05之 ConcurrentSkipListMap

来源:互联网 发布:php构架师是什么 编辑:程序博客网 时间:2024/05/24 06:08

概要

本章对Java.util.concurrent包中的ConcurrentSkipListMap类进行详细的介绍。内容包括:
ConcurrentSkipListMap介绍
ConcurrentSkipListMap原理和数据结构
ConcurrentSkipListMap函数列表
ConcurrentSkipListMap源码分析(JDK1.7.0_40版本)
ConcurrentSkipListMap示例

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3498556.html

 

ConcurrentSkipListMap介绍

ConcurrentSkipListMap是线程安全的有序的哈希表,适用于高并发的场景。
ConcurrentSkipListMap和TreeMap,它们虽然都是有序的哈希表。但是,第一,它们的线程安全机制不同,TreeMap是非线程安全的,而ConcurrentSkipListMap是线程安全的。第二,ConcurrentSkipListMap是通过跳表实现的,而TreeMap是通过红黑树实现的。
关于跳表(Skip List),它是平衡树的一种替代的数据结构,但是和红黑树不相同的是,跳表对于树的平衡的实现是基于一种随机化的算法的,这样也就是说跳表的插入和删除的工作是比较简单的。

 

ConcurrentSkipListMap原理和数据结构

ConcurrentSkipListMap的数据结构,如下图所示:

说明

先以数据“7,14,21,32,37,71,85”序列为例,来对跳表进行简单说明。

跳表分为许多层(level),每一层都可以看作是数据的索引,这些索引的意义就是加快跳表查找数据速度。每一层的数据都是有序的,上一层数据是下一层数据的子集,并且第一层(level 1)包含了全部的数据;层次越高,跳跃性越大,包含的数据越少。
跳表包含一个表头,它查找数据时,是从上往下,从左往右进行查找。现在“需要找出值为32的节点”为例,来对比说明跳表和普遍的链表。

情况1:链表中查找“32”节点
路径如下图1-02所示:

需要4步(红色部分表示路径)。

 

情况2:跳表中查找“32”节点
路径如下图1-03所示:

忽略索引垂直线路上路径的情况下,只需要2步(红色部分表示路径)。


下面说说Java中ConcurrentSkipListMap的数据结构。
(01) ConcurrentSkipListMap继承于AbstractMap类,也就意味着它是一个哈希表。
(02) Index是ConcurrentSkipListMap的内部类,它与“跳表中的索引相对应”。HeadIndex继承于Index,ConcurrentSkipListMap中含有一个HeadIndex的对象head,head是“跳表的表头”。
(03) Index是跳表中的索引,它包含“右索引的指针(right)”,“下索引的指针(down)”和“哈希表节点node”。node是Node的对象,Node也是ConcurrentSkipListMap中的内部类。

 

ConcurrentSkipListMap函数列表

复制代码
// 构造一个新的空映射,该映射按照键的自然顺序进行排序。ConcurrentSkipListMap()// 构造一个新的空映射,该映射按照指定的比较器进行排序。ConcurrentSkipListMap(Comparator<? super K> comparator)// 构造一个新映射,该映射所包含的映射关系与给定映射包含的映射关系相同,并按照键的自然顺序进行排序。ConcurrentSkipListMap(Map<? extends K,? extends V> m)// 构造一个新映射,该映射所包含的映射关系与指定的有序映射包含的映射关系相同,使用的顺序也相同。ConcurrentSkipListMap(SortedMap<K,? extends V> m)// 返回与大于等于给定键的最小键关联的键-值映射关系;如果不存在这样的条目,则返回 null。Map.Entry<K,V> ceilingEntry(K key)// 返回大于等于给定键的最小键;如果不存在这样的键,则返回 null。K ceilingKey(K key)// 从此映射中移除所有映射关系。void clear()// 返回此 ConcurrentSkipListMap 实例的浅表副本。ConcurrentSkipListMap<K,V> clone()// 返回对此映射中的键进行排序的比较器;如果此映射使用键的自然顺序,则返回 null。Comparator<? super K> comparator()// 如果此映射包含指定键的映射关系,则返回 true。boolean containsKey(Object key)// 如果此映射为指定值映射一个或多个键,则返回 true。boolean containsValue(Object value)// 返回此映射中所包含键的逆序 NavigableSet 视图。NavigableSet<K> descendingKeySet()// 返回此映射中所包含映射关系的逆序视图。ConcurrentNavigableMap<K,V> descendingMap()// 返回此映射中所包含的映射关系的 Set 视图。Set<Map.Entry<K,V>> entrySet()// 比较指定对象与此映射的相等性。boolean equals(Object o)// 返回与此映射中的最小键关联的键-值映射关系;如果该映射为空,则返回 null。Map.Entry<K,V> firstEntry()// 返回此映射中当前第一个(最低)键。K firstKey()// 返回与小于等于给定键的最大键关联的键-值映射关系;如果不存在这样的键,则返回 null。Map.Entry<K,V> floorEntry(K key)// 返回小于等于给定键的最大键;如果不存在这样的键,则返回 null。K floorKey(K key)// 返回指定键所映射到的值;如果此映射不包含该键的映射关系,则返回 null。V get(Object key)// 返回此映射的部分视图,其键值严格小于 toKey。ConcurrentNavigableMap<K,V> headMap(K toKey)// 返回此映射的部分视图,其键小于(或等于,如果 inclusive 为 true)toKey。ConcurrentNavigableMap<K,V> headMap(K toKey, boolean inclusive)// 返回与严格大于给定键的最小键关联的键-值映射关系;如果不存在这样的键,则返回 null。Map.Entry<K,V> higherEntry(K key)// 返回严格大于给定键的最小键;如果不存在这样的键,则返回 null。K higherKey(K key)// 如果此映射未包含键-值映射关系,则返回 true。boolean isEmpty()// 返回此映射中所包含键的 NavigableSet 视图。NavigableSet<K> keySet()// 返回与此映射中的最大键关联的键-值映射关系;如果该映射为空,则返回 null。Map.Entry<K,V> lastEntry()// 返回映射中当前最后一个(最高)键。K lastKey()// 返回与严格小于给定键的最大键关联的键-值映射关系;如果不存在这样的键,则返回 null。Map.Entry<K,V> lowerEntry(K key)// 返回严格小于给定键的最大键;如果不存在这样的键,则返回 null。K lowerKey(K key)// 返回此映射中所包含键的 NavigableSet 视图。NavigableSet<K> navigableKeySet()// 移除并返回与此映射中的最小键关联的键-值映射关系;如果该映射为空,则返回 null。Map.Entry<K,V> pollFirstEntry()// 移除并返回与此映射中的最大键关联的键-值映射关系;如果该映射为空,则返回 null。Map.Entry<K,V> pollLastEntry()// 将指定值与此映射中的指定键关联。V put(K key, V value)// 如果指定键已经不再与某个值相关联,则将它与给定值关联。V putIfAbsent(K key, V value)// 从此映射中移除指定键的映射关系(如果存在)。V remove(Object key)// 只有目前将键的条目映射到给定值时,才移除该键的条目。boolean remove(Object key, Object value)// 只有目前将键的条目映射到某一值时,才替换该键的条目。V replace(K key, V value)// 只有目前将键的条目映射到给定值时,才替换该键的条目。boolean replace(K key, V oldValue, V newValue)// 返回此映射中的键-值映射关系数。int size()// 返回此映射的部分视图,其键的范围从 fromKey 到 toKey。ConcurrentNavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)// 返回此映射的部分视图,其键值的范围从 fromKey(包括)到 toKey(不包括)。ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey)// 返回此映射的部分视图,其键大于等于 fromKey。ConcurrentNavigableMap<K,V> tailMap(K fromKey)// 返回此映射的部分视图,其键大于(或等于,如果 inclusive 为 true)fromKey。ConcurrentNavigableMap<K,V> tailMap(K fromKey, boolean inclusive)// 返回此映射中所包含值的 Collection 视图。Collection<V> values()
复制代码

 

ConcurrentSkipListMap源码分析(JDK1.7.0_40版本)

ConcurrentSkipListMap.java的完整源码如下:

复制代码
   1 /*   2  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.   3  *   4  *   5  *   6  *   7  *   8  *   9  *  10  *  11  *  12  *  13  *  14  *  15  *  16  *  17  *  18  *  19  *  20  *  21  *  22  *  23  */  24   25 /*  26  *  27  *  28  *  29  *  30  *  31  * Written by Doug Lea with assistance from members of JCP JSR-166  32  * Expert Group and released to the public domain, as explained at  33  * http://creativecommons.org/publicdomain/zero/1.0/  34  */  35   36 package java.util.concurrent;  37 import java.util.*;  38 import java.util.concurrent.atomic.*;  39   40 /**  41  * A scalable concurrent {@link ConcurrentNavigableMap} implementation.  42  * The map is sorted according to the {@linkplain Comparable natural  43  * ordering} of its keys, or by a {@link Comparator} provided at map  44  * creation time, depending on which constructor is used.  45  *  46  * <p>This class implements a concurrent variant of <a  47  * href="http://en.wikipedia.org/wiki/Skip_list" target="_top">SkipLists</a>  48  * providing expected average <i>log(n)</i> time cost for the  49  * <tt>containsKey</tt>, <tt>get</tt>, <tt>put</tt> and  50  * <tt>remove</tt> operations and their variants.  Insertion, removal,  51  * update, and access operations safely execute concurrently by  52  * multiple threads.  Iterators are <i>weakly consistent</i>, returning  53  * elements reflecting the state of the map at some point at or since  54  * the creation of the iterator.  They do <em>not</em> throw {@link  55  * ConcurrentModificationException}, and may proceed concurrently with  56  * other operations. Ascending key ordered views and their iterators  57  * are faster than descending ones.  58  *  59  * <p>All <tt>Map.Entry</tt> pairs returned by methods in this class  60  * and its views represent snapshots of mappings at the time they were  61  * produced. They do <em>not</em> support the <tt>Entry.setValue</tt>  62  * method. (Note however that it is possible to change mappings in the  63  * associated map using <tt>put</tt>, <tt>putIfAbsent</tt>, or  64  * <tt>replace</tt>, depending on exactly which effect you need.)  65  *  66  * <p>Beware that, unlike in most collections, the <tt>size</tt>  67  * method is <em>not</em> a constant-time operation. Because of the  68  * asynchronous nature of these maps, determining the current number  69  * of elements requires a traversal of the elements, and so may report  70  * inaccurate results if this collection is modified during traversal.  71  * Additionally, the bulk operations <tt>putAll</tt>, <tt>equals</tt>,  72  * <tt>toArray</tt>, <tt>containsValue</tt>, and <tt>clear</tt> are  73  * <em>not</em> guaranteed to be performed atomically. For example, an  74  * iterator operating concurrently with a <tt>putAll</tt> operation  75  * might view only some of the added elements.  76  *  77  * <p>This class and its views and iterators implement all of the  78  * <em>optional</em> methods of the {@link Map} and {@link Iterator}  79  * interfaces. Like most other concurrent collections, this class does  80  * <em>not</em> permit the use of <tt>null</tt> keys or values because some  81  * null return values cannot be reliably distinguished from the absence of  82  * elements.  83  *  84  * <p>This class is a member of the  85  * <a href="{@docRoot}/../technotes/guides/collections/index.html">  86  * Java Collections Framework</a>.  87  *  88  * @author Doug Lea  89  * @param <K> the type of keys maintained by this map  90  * @param <V> the type of mapped values  91  * @since 1.6  92  */  93 public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>  94     implements ConcurrentNavigableMap<K,V>,  95                Cloneable,  96                java.io.Serializable {  97     /*  98      * This class implements a tree-like two-dimensionally linked skip  99      * list in which the index levels are represented in separate 100      * nodes from the base nodes holding data.  There are two reasons 101      * for taking this approach instead of the usual array-based 102      * structure: 1) Array based implementations seem to encounter 103      * more complexity and overhead 2) We can use cheaper algorithms 104      * for the heavily-traversed index lists than can be used for the 105      * base lists.  Here's a picture of some of the basics for a 106      * possible list with 2 levels of index: 107      * 108      * Head nodes          Index nodes 109      * +-+    right        +-+                      +-+ 110      * |2|---------------->| |--------------------->| |->null 111      * +-+                 +-+                      +-+ 112      *  | down              |                        | 113      *  v                   v                        v 114      * +-+            +-+  +-+       +-+            +-+       +-+ 115      * |1|----------->| |->| |------>| |----------->| |------>| |->null 116      * +-+            +-+  +-+       +-+            +-+       +-+ 117      *  v              |    |         |              |         | 118      * Nodes  next     v    v         v              v         v 119      * +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+ 120      * | |->|A|->|B|->|C|->|D|->|E|->|F|->|G|->|H|->|I|->|J|->|K|->null 121      * +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+  +-+ 122      * 123      * The base lists use a variant of the HM linked ordered set 124      * algorithm. See Tim Harris, "A pragmatic implementation of 125      * non-blocking linked lists" 126      * http://www.cl.cam.ac.uk/~tlh20/publications.html and Maged 127      * Michael "High Performance Dynamic Lock-Free Hash Tables and 128      * List-Based Sets" 129      * http://www.research.ibm.com/people/m/michael/pubs.htm.  The 130      * basic idea in these lists is to mark the "next" pointers of 131      * deleted nodes when deleting to avoid conflicts with concurrent 132      * insertions, and when traversing to keep track of triples 133      * (predecessor, node, successor) in order to detect when and how 134      * to unlink these deleted nodes. 135      * 136      * Rather than using mark-bits to mark list deletions (which can 137      * be slow and space-intensive using AtomicMarkedReference), nodes 138      * use direct CAS'able next pointers.  On deletion, instead of 139      * marking a pointer, they splice in another node that can be 140      * thought of as standing for a marked pointer (indicating this by 141      * using otherwise impossible field values).  Using plain nodes 142      * acts roughly like "boxed" implementations of marked pointers, 143      * but uses new nodes only when nodes are deleted, not for every 144      * link.  This requires less space and supports faster 145      * traversal. Even if marked references were better supported by 146      * JVMs, traversal using this technique might still be faster 147      * because any search need only read ahead one more node than 148      * otherwise required (to check for trailing marker) rather than 149      * unmasking mark bits or whatever on each read. 150      * 151      * This approach maintains the essential property needed in the HM 152      * algorithm of changing the next-pointer of a deleted node so 153      * that any other CAS of it will fail, but implements the idea by 154      * changing the pointer to point to a different node, not by 155      * marking it.  While it would be possible to further squeeze 156      * space by defining marker nodes not to have key/value fields, it 157      * isn't worth the extra type-testing overhead.  The deletion 158      * markers are rarely encountered during traversal and are 159      * normally quickly garbage collected. (Note that this technique 160      * would not work well in systems without garbage collection.) 161      * 162      * In addition to using deletion markers, the lists also use 163      * nullness of value fields to indicate deletion, in a style 164      * similar to typical lazy-deletion schemes.  If a node's value is 165      * null, then it is considered logically deleted and ignored even 166      * though it is still reachable. This maintains proper control of 167      * concurrent replace vs delete operations -- an attempted replace 168      * must fail if a delete beat it by nulling field, and a delete 169      * must return the last non-null value held in the field. (Note: 170      * Null, rather than some special marker, is used for value fields 171      * here because it just so happens to mesh with the Map API 172      * requirement that method get returns null if there is no 173      * mapping, which allows nodes to remain concurrently readable 174      * even when deleted. Using any other marker value here would be 175      * messy at best.) 176      * 177      * Here's the sequence of events for a deletion of node n with 178      * predecessor b and successor f, initially: 179      * 180      *        +------+       +------+      +------+ 181      *   ...  |   b  |------>|   n  |----->|   f  | ... 182      *        +------+       +------+      +------+ 183      * 184      * 1. CAS n's value field from non-null to null. 185      *    From this point on, no public operations encountering 186      *    the node consider this mapping to exist. However, other 187      *    ongoing insertions and deletions might still modify 188      *    n's next pointer. 189      * 190      * 2. CAS n's next pointer to point to a new marker node. 191      *    From this point on, no other nodes can be appended to n. 192      *    which avoids deletion errors in CAS-based linked lists. 193      * 194      *        +------+       +------+      +------+       +------+ 195      *   ...  |   b  |------>|   n  |----->|marker|------>|   f  | ... 196      *        +------+       +------+      +------+       +------+ 197      * 198      * 3. CAS b's next pointer over both n and its marker. 199      *    From this point on, no new traversals will encounter n, 200      *    and it can eventually be GCed. 201      *        +------+                                    +------+ 202      *   ...  |   b  |----------------------------------->|   f  | ... 203      *        +------+                                    +------+ 204      * 205      * A failure at step 1 leads to simple retry due to a lost race 206      * with another operation. Steps 2-3 can fail because some other 207      * thread noticed during a traversal a node with null value and 208      * helped out by marking and/or unlinking.  This helping-out 209      * ensures that no thread can become stuck waiting for progress of 210      * the deleting thread.  The use of marker nodes slightly 211      * complicates helping-out code because traversals must track 212      * consistent reads of up to four nodes (b, n, marker, f), not 213      * just (b, n, f), although the next field of a marker is 214      * immutable, and once a next field is CAS'ed to point to a 215      * marker, it never again changes, so this requires less care. 216      * 217      * Skip lists add indexing to this scheme, so that the base-level 218      * traversals start close to the locations being found, inserted 219      * or deleted -- usually base level traversals only traverse a few 220      * nodes. This doesn't change the basic algorithm except for the 221      * need to make sure base traversals start at predecessors (here, 222      * b) that are not (structurally) deleted, otherwise retrying 223      * after processing the deletion. 224      * 225      * Index levels are maintained as lists with volatile next fields, 226      * using CAS to link and unlink.  Races are allowed in index-list 227      * operations that can (rarely) fail to link in a new index node 228      * or delete one. (We can't do this of course for data nodes.) 229      * However, even when this happens, the index lists remain sorted, 230      * so correctly serve as indices.  This can impact performance, 231      * but since skip lists are probabilistic anyway, the net result 232      * is that under contention, the effective "p" value may be lower 233      * than its nominal value. And race windows are kept small enough 234      * that in practice these failures are rare, even under a lot of 235      * contention. 236      * 237      * The fact that retries (for both base and index lists) are 238      * relatively cheap due to indexing allows some minor 239      * simplifications of retry logic. Traversal restarts are 240      * performed after most "helping-out" CASes. This isn't always 241      * strictly necessary, but the implicit backoffs tend to help 242      * reduce other downstream failed CAS's enough to outweigh restart 243      * cost.  This worsens the worst case, but seems to improve even 244      * highly contended cases. 245      * 246      * Unlike most skip-list implementations, index insertion and 247      * deletion here require a separate traversal pass occuring after 248      * the base-level action, to add or remove index nodes.  This adds 249      * to single-threaded overhead, but improves contended 250      * multithreaded performance by narrowing interference windows, 251      * and allows deletion to ensure that all index nodes will be made 252      * unreachable upon return from a public remove operation, thus 253      * avoiding unwanted garbage retention. This is more important 254      * here than in some other data structures because we cannot null 255      * out node fields referencing user keys since they might still be 256      * read by other ongoing traversals. 257      * 258      * Indexing uses skip list parameters that maintain good search 259      * performance while using sparser-than-usual indices: The 260      * hardwired parameters k=1, p=0.5 (see method randomLevel) mean 261      * that about one-quarter of the nodes have indices. Of those that 262      * do, half have one level, a quarter have two, and so on (see 263      * Pugh's Skip List Cookbook, sec 3.4).  The expected total space 264      * requirement for a map is slightly less than for the current 265      * implementation of java.util.TreeMap. 266      * 267      * Changing the level of the index (i.e, the height of the 268      * tree-like structure) also uses CAS. The head index has initial 269      * level/height of one. Creation of an index with height greater 270      * than the current level adds a level to the head index by 271      * CAS'ing on a new top-most head. To maintain good performance 272      * after a lot of removals, deletion methods heuristically try to 273      * reduce the height if the topmost levels appear to be empty. 274      * This may encounter races in which it possible (but rare) to 275      * reduce and "lose" a level just as it is about to contain an 276      * index (that will then never be encountered). This does no 277      * structural harm, and in practice appears to be a better option 278      * than allowing unrestrained growth of levels. 279      * 280      * The code for all this is more verbose than you'd like. Most 281      * operations entail locating an element (or position to insert an 282      * element). The code to do this can't be nicely factored out 283      * because subsequent uses require a snapshot of predecessor 284      * and/or successor and/or value fields which can't be returned 285      * all at once, at least not without creating yet another object 286      * to hold them -- creating such little objects is an especially 287      * bad idea for basic internal search operations because it adds 288      * to GC overhead.  (This is one of the few times I've wished Java 289      * had macros.) Instead, some traversal code is interleaved within 290      * insertion and removal operations.  The control logic to handle 291      * all the retry conditions is sometimes twisty. Most search is 292      * broken into 2 parts. findPredecessor() searches index nodes 293      * only, returning a base-level predecessor of the key. findNode() 294      * finishes out the base-level search. Even with this factoring, 295      * there is a fair amount of near-duplication of code to handle 296      * variants. 297      * 298      * For explanation of algorithms sharing at least a couple of 299      * features with this one, see Mikhail Fomitchev's thesis 300      * (http://www.cs.yorku.ca/~mikhail/), Keir Fraser's thesis 301      * (http://www.cl.cam.ac.uk/users/kaf24/), and Hakan Sundell's 302      * thesis (http://www.cs.chalmers.se/~phs/). 303      * 304      * Given the use of tree-like index nodes, you might wonder why 305      * this doesn't use some kind of search tree instead, which would 306      * support somewhat faster search operations. The reason is that 307      * there are no known efficient lock-free insertion and deletion 308      * algorithms for search trees. The immutability of the "down" 309      * links of index nodes (as opposed to mutable "left" fields in 310      * true trees) makes this tractable using only CAS operations. 311      * 312      * Notation guide for local variables 313      * Node:         b, n, f    for  predecessor, node, successor 314      * Index:        q, r, d    for index node, right, down. 315      *               t          for another index node 316      * Head:         h 317      * Levels:       j 318      * Keys:         k, key 319      * Values:       v, value 320      * Comparisons:  c 321      */ 322  323     private static final long serialVersionUID = -8627078645895051609L; 324  325     /** 326      * Generates the initial random seed for the cheaper per-instance 327      * random number generators used in randomLevel. 328      */ 329     private static final Random seedGenerator = new Random(); 330  331     /** 332      * Special value used to identify base-level header 333      */ 334     private static final Object BASE_HEADER = new Object(); 335  336     /** 337      * The topmost head index of the skiplist. 338      */ 339     private transient volatile HeadIndex<K,V> head; 340  341     /** 342      * The comparator used to maintain order in this map, or null 343      * if using natural ordering. 344      * @serial 345      */ 346     private final Comparator<? super K> comparator; 347  348     /** 349      * Seed for simple random number generator.  Not volatile since it 350      * doesn't matter too much if different threads don't see updates. 351      */ 352     private transient int randomSeed; 353  354     /** Lazily initialized key set */ 355     private transient KeySet keySet; 356     /** Lazily initialized entry set */ 357     private transient EntrySet entrySet; 358     /** Lazily initialized values collection */ 359     private transient Values values; 360     /** Lazily initialized descending key set */ 361     private transient ConcurrentNavigableMap<K,V> descendingMap; 362  363     /** 364      * Initializes or resets state. Needed by constructors, clone, 365      * clear, readObject. and ConcurrentSkipListSet.clone. 366      * (Note that comparator must be separately initialized.) 367      */ 368     final void initialize() { 369         keySet = null; 370         entrySet = null; 371         values = null; 372         descendingMap = null; 373         randomSeed = seedGenerator.nextInt() | 0x0100; // ensure nonzero 374         head = new HeadIndex<K,V>(new Node<K,V>(null, BASE_HEADER, null), 375                                   null, null, 1); 376     } 377  378     /** 379      * compareAndSet head node 380      */ 381     private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) { 382         return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); 383     } 384  385     /* ---------------- Nodes -------------- */ 386  387     /** 388      * Nodes hold keys and values, and are singly linked in sorted 389      * order, possibly with some intervening marker nodes. The list is 390      * headed by a dummy node accessible as head.node. The value field 391      * is declared only as Object because it takes special non-V 392      * values for marker and header nodes. 393      */ 394     static final class Node<K,V> { 395         final K key; 396         volatile Object value; 397         volatile Node<K,V> next; 398  399         /** 400          * Creates a new regular node. 401          */ 402         Node(K key, Object value, Node<K,V> next) { 403             this.key = key; 404             this.value = value; 405             this.next = next; 406         } 407  408         /** 409          * Creates a new marker node. A marker is distinguished by 410          * having its value field point to itself.  Marker nodes also 411          * have null keys, a fact that is exploited in a few places, 412          * but this doesn't distinguish markers from the base-level 413          * header node (head.node), which also has a null key. 414          */ 415         Node(Node<K,V> next) { 416             this.key = null; 417             this.value = this; 418             this.next = next; 419         } 420  421         /** 422          * compareAndSet value field 423          */ 424         boolean casValue(Object cmp, Object val) { 425             return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val); 426         } 427  428         /** 429          * compareAndSet next field 430          */ 431         boolean casNext(Node<K,V> cmp, Node<K,V> val) { 432             return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); 433         } 434  435         /** 436          * Returns true if this node is a marker. This method isn't 437          * actually called in any current code checking for markers 438          * because callers will have already read value field and need 439          * to use that read (not another done here) and so directly 440          * test if value points to node. 441          * @param n a possibly null reference to a node 442          * @return true if this node is a marker node 443          */ 444         boolean isMarker() { 445             return value == this; 446         } 447  448         /** 449          * Returns true if this node is the header of base-level list. 450          * @return true if this node is header node 451          */ 452         boolean isBaseHeader() { 453             return value == BASE_HEADER; 454         } 455  456         /** 457          * Tries to append a deletion marker to this node. 458          * @param f the assumed current successor of this node 459          * @return true if successful 460          */ 461         boolean appendMarker(Node<K,V> f) { 462             return casNext(f, new Node<K,V>(f)); 463         } 464  465         /** 466          * Helps out a deletion by appending marker or unlinking from 467          * predecessor. This is called during traversals when value 468          * field seen to be null. 469          * @param b predecessor 470          * @param f successor 471          */ 472         void helpDelete(Node<K,V> b, Node<K,V> f) { 473             /* 474              * Rechecking links and then doing only one of the 475              * help-out stages per call tends to minimize CAS 476              * interference among helping threads. 477              */ 478             if (f == next && this == b.next) { 479                 if (f == null || f.value != f) // not already marked 480                     appendMarker(f); 481                 else 482                     b.casNext(this, f.next); 483             } 484         } 485  486         /** 487          * Returns value if this node contains a valid key-value pair, 488          * else null. 489          * @return this node's value if it isn't a marker or header or 490          * is deleted, else null. 491          */ 492         V getValidValue() { 493             Object v = value; 494             if (v == this || v == BASE_HEADER) 495                 return null; 496             return (V)v; 497         } 498  499         /** 500          * Creates and returns a new SimpleImmutableEntry holding current 501          * mapping if this node holds a valid value, else null. 502          * @return new entry or null 503          */ 504         AbstractMap.SimpleImmutableEntry<K,V> createSnapshot() { 505             V v = getValidValue(); 506             if (v == null) 507                 return null; 508             return new AbstractMap.SimpleImmutableEntry<K,V>(key, v); 509         } 510  511         // UNSAFE mechanics 512  513         private static final sun.misc.Unsafe UNSAFE; 514         private static final long valueOffset; 515         private static final long nextOffset; 516  517         static { 518             try { 519                 UNSAFE = sun.misc.Unsafe.getUnsafe(); 520                 Class k = Node.class; 521                 valueOffset = UNSAFE.objectFieldOffset 522                     (k.getDeclaredField("value")); 523                 nextOffset = UNSAFE.objectFieldOffset 524                     (k.getDeclaredField("next")); 525             } catch (Exception e) { 526                 throw new Error(e); 527             } 528         } 529     } 530  531     /* ---------------- Indexing -------------- */ 532  533     /** 534      * Index nodes represent the levels of the skip list.  Note that 535      * even though both Nodes and Indexes have forward-pointing 536      * fields, they have different types and are handled in different 537      * ways, that can't nicely be captured by placing field in a 538      * shared abstract class. 539      */ 540     static class Index<K,V> { 541         final Node<K,V> node; 542         final Index<K,V> down; 543         volatile Index<K,V> right; 544  545         /** 546          * Creates index node with given values. 547          */ 548         Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) { 549             this.node = node; 550             this.down = down; 551             this.right = right; 552         } 553  554         /** 555          * compareAndSet right field 556          */ 557         final boolean casRight(Index<K,V> cmp, Index<K,V> val) { 558             return UNSAFE.compareAndSwapObject(this, rightOffset, cmp, val); 559         } 560  561         /** 562          * Returns true if the node this indexes has been deleted. 563          * @return true if indexed node is known to be deleted 564          */ 565         final boolean indexesDeletedNode() { 566             return node.value == null; 567         } 568  569         /** 570          * Tries to CAS newSucc as successor.  To minimize races with 571          * unlink that may lose this index node, if the node being 572          * indexed is known to be deleted, it doesn't try to link in. 573          * @param succ the expected current successor 574          * @param newSucc the new successor 575          * @return true if successful 576          */ 577         final boolean link(Index<K,V> succ, Index<K,V> newSucc) { 578             Node<K,V> n = node; 579             newSucc.right = succ; 580             return n.value != null && casRight(succ, newSucc); 581         } 582  583         /** 584          * Tries to CAS right field to skip over apparent successor 585          * succ.  Fails (forcing a retraversal by caller) if this node 586          * is known to be deleted. 587          * @param succ the expected current successor 588          * @return true if successful 589          */ 590         final boolean unlink(Index<K,V> succ) { 591             return !indexesDeletedNode() && casRight(succ, succ.right); 592         } 593  594         // Unsafe mechanics 595         private static final sun.misc.Unsafe UNSAFE; 596         private static final long rightOffset; 597         static { 598             try { 599                 UNSAFE = sun.misc.Unsafe.getUnsafe(); 600                 Class k = Index.class; 601                 rightOffset = UNSAFE.objectFieldOffset 602                     (k.getDeclaredField("right")); 603             } catch (Exception e) { 604                 throw new Error(e); 605             } 606         } 607     } 608  609     /* ---------------- Head nodes -------------- */ 610  611     /** 612      * Nodes heading each level keep track of their level. 613      */ 614     static final class HeadIndex<K,V> extends Index<K,V> { 615         final int level; 616         HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) { 617             super(node, down, right); 618             this.level = level; 619         } 620     } 621  622     /* ---------------- Comparison utilities -------------- */ 623  624     /** 625      * Represents a key with a comparator as a Comparable. 626      * 627      * Because most sorted collections seem to use natural ordering on 628      * Comparables (Strings, Integers, etc), most internal methods are 629      * geared to use them. This is generally faster than checking 630      * per-comparison whether to use comparator or comparable because 631      * it doesn't require a (Comparable) cast for each comparison. 632      * (Optimizers can only sometimes remove such redundant checks 633      * themselves.) When Comparators are used, 634      * ComparableUsingComparators are created so that they act in the 635      * same way as natural orderings. This penalizes use of 636      * Comparators vs Comparables, which seems like the right 637      * tradeoff. 638      */ 639     static final class ComparableUsingComparator<K> implements Comparable<K> { 640         final K actualKey; 641         final Comparator<? super K> cmp; 642         ComparableUsingComparator(K key, Comparator<? super K> cmp) { 643             this.actualKey = key; 644             this.cmp = cmp; 645         } 646         public int compareTo(K k2) { 647             return cmp.compare(actualKey, k2); 648         } 649     } 650  651     /** 652      * If using comparator, return a ComparableUsingComparator, else 653      * cast key as Comparable, which may cause ClassCastException, 654      * which is propagated back to caller. 655      */ 656     private Comparable<? super K> comparable(Object key) 657             throws ClassCastException { 658         if (key == null) 659             throw new NullPointerException(); 660         if (comparator != null) 661             return new ComparableUsingComparator<K>((K)key, comparator); 662         else 663             return (Comparable<? super K>)key; 664     } 665  666     /** 667      * Compares using comparator or natural ordering. Used when the 668      * ComparableUsingComparator approach doesn't apply. 669      */ 670     int compare(K k1, K k2) throws ClassCastException { 671         Comparator<? super K> cmp = comparator; 672         if (cmp != null) 673             return cmp.compare(k1, k2); 674         else 675             return ((Comparable<? super K>)k1).compareTo(k2); 676     } 677  678     /** 679      * Returns true if given key greater than or equal to least and 680      * strictly less than fence, bypassing either test if least or 681      * fence are null. Needed mainly in submap operations. 682      */ 683     boolean inHalfOpenRange(K key, K least, K fence) { 684         if (key == null) 685             throw new NullPointerException(); 686         return ((least == null || compare(key, least) >= 0) && 687                 (fence == null || compare(key, fence) <  0)); 688     } 689  690     /** 691      * Returns true if given key greater than or equal to least and less 692      * or equal to fence. Needed mainly in submap operations. 693      */ 694     boolean inOpenRange(K key, K least, K fence) { 695         if (key == null) 696             throw new NullPointerException(); 697         return ((least == null || compare(key, least) >= 0) && 698                 (fence == null || compare(key, fence) <= 0)); 699     } 700  701     /* ---------------- Traversal -------------- */ 702  703     /** 704      * Returns a base-level node with key strictly less than given key, 705      * or the base-level header if there is no such node.  Also 706      * unlinks indexes to deleted nodes found along the way.  Callers 707      * rely on this side-effect of clearing indices to deleted nodes. 708      * @param key the key 709      * @return a predecessor of key 710      */ 711     private Node<K,V> findPredecessor(Comparable<? super K> key) { 712         if (key == null) 713             throw new NullPointerException(); // don't postpone errors 714         for (;;) { 715             Index<K,V> q = head; 716             Index<K,V> r = q.right; 717             for (;;) { 718                 if (r != null) { 719                     Node<K,V> n = r.node; 720                     K k = n.key; 721                     if (n.value == null) { 722                         if (!q.unlink(r)) 723                             break;           // restart 724                         r = q.right;         // reread r 725                         continue; 726                     } 727                     if (key.compareTo(k) > 0) { 728                         q = r; 729                         r = r.right; 730                         continue; 731                     } 732                 } 733                 Index<K,V> d = q.down; 734                 if (d != null) { 735                     q = d; 736                     r = d.right; 737                 } else 738                     return q.node; 739             } 740         } 741     } 742  743     /** 744      * Returns node holding key or null if no such, clearing out any 745      * deleted nodes seen along the way.  Repeatedly traverses at 746      * base-level looking for key starting at predecessor returned 747      * from findPredecessor, processing base-level deletions as 748      * encountered. Some callers rely on this side-effect of clearing 749      * deleted nodes. 750      * 751      * Restarts occur, at traversal step centered on node n, if: 752      * 753      *   (1) After reading n's next field, n is no longer assumed 754      *       predecessor b's current successor, which means that 755      *       we don't have a consistent 3-node snapshot and so cannot 756      *       unlink any subsequent deleted nodes encountered. 757      * 758      *   (2) n's value field is null, indicating n is deleted, in 759      *       which case we help out an ongoing structural deletion 760      *       before retrying.  Even though there are cases where such 761      *       unlinking doesn't require restart, they aren't sorted out 762      *       here because doing so would not usually outweigh cost of 763      *       restarting. 764      * 765      *   (3) n is a marker or n's predecessor's value field is null, 766      *       indicating (among other possibilities) that 767      *       findPredecessor returned a deleted node. We can't unlink 768      *       the node because we don't know its predecessor, so rely 769      *       on another call to findPredecessor to notice and return 770      *       some earlier predecessor, which it will do. This check is 771      *       only strictly needed at beginning of loop, (and the 772      *       b.value check isn't strictly needed at all) but is done 773      *       each iteration to help avoid contention with other 774      *       threads by callers that will fail to be able to change 775      *       links, and so will retry anyway. 776      * 777      * The traversal loops in doPut, doRemove, and findNear all 778      * include the same three kinds of checks. And specialized 779      * versions appear in findFirst, and findLast and their 780      * variants. They can't easily share code because each uses the 781      * reads of fields held in locals occurring in the orders they 782      * were performed. 783      * 784      * @param key the key 785      * @return node holding key, or null if no such 786      */ 787     private Node<K,V> findNode(Comparable<? super K> key) { 788         for (;;) { 789             Node<K,V> b = findPredecessor(key); 790             Node<K,V> n = b.next; 791             for (;;) { 792                 if (n == null) 793                     return null; 794                 Node<K,V> f = n.next; 795                 if (n != b.next)                // inconsistent read 796                     break; 797                 Object v = n.value; 798                 if (v == null) {                // n is deleted 799                     n.helpDelete(b, f); 800                     break; 801                 } 802                 if (v == n || b.value == null)  // b is deleted 803                     break; 804                 int c = key.compareTo(n.key); 805                 if (c == 0) 806                     return n; 807                 if (c < 0) 808                     return null; 809                 b = n; 810                 n = f; 811             } 812         } 813     } 814  815     /** 816      * Gets value for key using findNode. 817      * @param okey the key 818      * @return the value, or null if absent 819      */ 820     private V doGet(Object okey) { 821         Comparable<? super K> key = comparable(okey); 822         /* 823          * Loop needed here and elsewhere in case value field goes 824          * null just as it is about to be returned, in which case we 825          * lost a race with a deletion, so must retry. 826          */ 827         for (;;) { 828             Node<K,V> n = findNode(key); 829             if (n == null) 830                 return null; 831             Object v = n.value; 832             if (v != null) 833                 return (V)v; 834         } 835     } 836  837     /* ---------------- Insertion -------------- */ 838  839     /** 840      * Main insertion method.  Adds element if not present, or 841      * replaces value if present and onlyIfAbsent is false. 842      * @param kkey the key 843      * @param value  the value that must be associated with key 844      * @param onlyIfAbsent if should not insert if already present 845      * @return the old value, or null if newly inserted 846      */ 847     private V doPut(K kkey, V value, boolean onlyIfAbsent) { 848         Comparable<? super K> key = comparable(kkey); 849         for (;;) { 850             Node<K,V> b = findPredecessor(key); 851             Node<K,V> n = b.next; 852             for (;;) { 853                 if (n != null) { 854                     Node<K,V> f = n.next; 855                     if (n != b.next)               // inconsistent read 856                         break; 857                     Object v = n.value; 858                     if (v == null) {               // n is deleted 859                         n.helpDelete(b, f); 860                         break; 861                     } 862                     if (v == n || b.value == null) // b is deleted 863                         break; 864                     int c = key.compareTo(n.key); 865                     if (c > 0) { 866                         b = n; 867                         n = f; 868                         continue; 869                     } 870                     if (c == 0) { 871                         if (onlyIfAbsent || n.casValue(v, value)) 872                             return (V)v; 873                         else 874                             break; // restart if lost race to replace value 875                     } 876                     // else c < 0; fall through 877                 } 878  879                 Node<K,V> z = new Node<K,V>(kkey, value, n); 880                 if (!b.casNext(n, z)) 881                     break;         // restart if lost race to append to b 882                 int level = randomLevel(); 883                 if (level > 0) 884                     insertIndex(z, level); 885                 return null; 886             } 887         } 888     } 889  890     /** 891      * Returns a random level for inserting a new node. 892      * Hardwired to k=1, p=0.5, max 31 (see above and 893      * Pugh's "Skip List Cookbook", sec 3.4). 894      * 895      * This uses the simplest of the generators described in George 896      * Marsaglia's "Xorshift RNGs" paper.  This is not a high-quality 897      * generator but is acceptable here. 898      */ 899     private int randomLevel() { 900         int x = randomSeed; 901         x ^= x << 13; 902         x ^= x >>> 17; 903         randomSeed = x ^= x << 5; 904         if ((x & 0x80000001) != 0) // test highest and lowest bits 905             return 0; 906         int level = 1; 907         while (((x >>>= 1) & 1) != 0) ++level; 908         return level; 909     } 910  911     /** 912      * Creates and adds index nodes for the given node. 913      * @param z the node 914      * @param level the level of the index 915      */ 916     private void insertIndex(Node<K,V> z, int level) { 917         HeadIndex<K,V> h = head; 918         int max = h.level; 919  920         if (level <= max) { 921             Index<K,V> idx = null; 922             for (int i = 1; i <= level; ++i) 923                 idx = new Index<K,V>(z, idx, null); 924             addIndex(idx, h, level); 925  926         } else { // Add a new level 927             /* 928              * To reduce interference by other threads checking for 929              * empty levels in tryReduceLevel, new levels are added 930              * with initialized right pointers. Which in turn requires 931              * keeping levels in an array to access them while 932              * creating new head index nodes from the opposite 933              * direction. 934              */ 935             level = max + 1; 936             Index<K,V>[] idxs = (Index<K,V>[])new Index[level+1]; 937             Index<K,V> idx = null; 938             for (int i = 1; i <= level; ++i) 939                 idxs[i] = idx = new Index<K,V>(z, idx, null); 940  941             HeadIndex<K,V> oldh; 942             int k; 943             for (;;) { 944                 oldh = head; 945                 int oldLevel = oldh.level; 946                 if (level <= oldLevel) { // lost race to add level 947                     k = level; 948                     break; 949                 } 950                 HeadIndex<K,V> newh = oldh; 951                 Node<K,V> oldbase = oldh.node; 952                 for (int j = oldLevel+1; j <= level; ++j) 953                     newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j); 954                 if (casHead(oldh, newh)) { 955                     k = oldLevel; 956                     break; 957                 } 958             } 959             addIndex(idxs[k], oldh, k); 960         } 961     } 962  963     /** 964      * Adds given index nodes from given level down to 1. 965      * @param idx the topmost index node being inserted 966      * @param h the value of head to use to insert. This must be 967      * snapshotted by callers to provide correct insertion level 968      * @param indexLevel the level of the index 969      */ 970     private void addIndex(Index<K,V> idx, HeadIndex<K,V> h, int indexLevel) { 971         // Track next level to insert in case of retries 972         int insertionLevel = indexLevel; 973         Comparable<? super K> key = comparable(idx.node.key); 974         if (key == null) throw new NullPointerException(); 975  976         // Similar to findPredecessor, but adding index nodes along 977         // path to key. 978         for (;;) { 979             int j = h.level; 980             Index<K,V> q = h; 981             Index<K,V> r = q.right; 982             Index<K,V> t = idx; 983             for (;;) { 984                 if (r != null) { 985                     Node<K,V> n = r.node; 986                     // compare before deletion check avoids needing recheck 987                     int c = key.compareTo(n.key); 988                     if (n.value == null) { 989                         if (!q.unlink(r)) 990                             break; 991                         r = q.right; 992                         continue; 993                     } 994                     if (c > 0) { 995                         q = r; 996                         r = r.right; 997                         continue; 998                     } 999                 }1000 1001                 if (j == insertionLevel) {1002                     // Don't insert index if node already deleted1003                     if (t.indexesDeletedNode()) {1004                         findNode(key); // cleans up1005                         return;1006                     }1007                     if (!q.link(r, t))1008                         break; // restart1009                     if (--insertionLevel == 0) {1010                         // need final deletion check before return1011                         if (t.indexesDeletedNode())1012                             findNode(key);1013                         return;1014                     }1015                 }1016 1017                 if (--j >= insertionLevel && j < indexLevel)1018                     t = t.down;1019                 q = q.down;1020                 r = q.right;1021             }1022         }1023     }1024 1025     /* ---------------- Deletion -------------- */1026 1027     /**1028      * Main deletion method. Locates node, nulls value, appends a1029      * deletion marker, unlinks predecessor, removes associated index1030      * nodes, and possibly reduces head index level.1031      *1032      * Index nodes are cleared out simply by calling findPredecessor.1033      * which unlinks indexes to deleted nodes found along path to key,1034      * which will include the indexes to this node.  This is done1035      * unconditionally. We can't check beforehand whether there are1036      * index nodes because it might be the case that some or all1037      * indexes hadn't been inserted yet for this node during initial1038      * search for it, and we'd like to ensure lack of garbage1039      * retention, so must call to be sure.1040      *1041      * @param okey the key1042      * @param value if non-null, the value that must be1043      * associated with key1044      * @return the node, or null if not found1045      */1046     final V doRemove(Object okey, Object value) {1047         Comparable<? super K> key = comparable(okey);1048         for (;;) {1049             Node<K,V> b = findPredecessor(key);1050             Node<K,V> n = b.next;1051             for (;;) {1052                 if (n == null)1053                     return null;1054                 Node<K,V> f = n.next;1055                 if (n != b.next)                    // inconsistent read1056                     break;1057                 Object v = n.value;1058                 if (v == null) {                    // n is deleted1059                     n.helpDelete(b, f);1060                     break;1061                 }1062                 if (v == n || b.value == null)      // b is deleted1063                     break;1064                 int c = key.compareTo(n.key);1065                 if (c < 0)1066                     return null;1067                 if (c > 0) {1068                     b = n;1069                     n = f;1070                     continue;1071                 }1072                 if (value != null && !value.equals(v))1073                     return null;1074                 if (!n.casValue(v, null))1075                     break;1076                 if (!n.appendMarker(f) || !b.casNext(n, f))1077                     findNode(key);                  // Retry via findNode1078                 else {1079                     findPredecessor(key);           // Clean index1080                     if (head.right == null)1081                         tryReduceLevel();1082                 }1083                 return (V)v;1084             }1085         }1086     }1087 1088     /**1089      * Possibly reduce head level if it has no nodes.  This method can1090      * (rarely) make mistakes, in which case levels can disappear even1091      * though they are about to contain index nodes. This impacts1092      * performance, not correctness.  To minimize mistakes as well as1093      * to reduce hysteresis, the level is reduced by one only if the1094      * topmost three levels look empty. Also, if the removed level1095      * looks non-empty after CAS, we try to change it back quick1096      * before anyone notices our mistake! (This trick works pretty1097      * well because this method will practically never make mistakes1098      * unless current thread stalls immediately before first CAS, in1099      * which case it is very unlikely to stall again immediately1100      * afterwards, so will recover.)1101      *1102      * We put up with all this rather than just let levels grow1103      * because otherwise, even a small map that has undergone a large1104      * number of insertions and removals will have a lot of levels,1105      * slowing down access more than would an occasional unwanted1106      * reduction.1107      */1108     private void tryReduceLevel() {1109         HeadIndex<K,V> h = head;1110         HeadIndex<K,V> d;1111         HeadIndex<K,V> e;1112         if (h.level > 3 &&1113             (d = (HeadIndex<K,V>)h.down) != null &&1114             (e = (HeadIndex<K,V>)d.down) != null &&1115             e.right == null &&1116             d.right == null &&1117             h.right == null &&1118             casHead(h, d) && // try to set1119             h.right != null) // recheck1120             casHead(d, h);   // try to backout1121     }1122 1123     /* ---------------- Finding and removing first element -------------- */1124 1125     /**1126      * Specialized variant of findNode to get first valid node.1127      * @return first node or null if empty1128      */1129     Node<K,V> findFirst() {1130         for (;;) {1131             Node<K,V> b = head.node;1132             Node<K,V> n = b.next;1133             if (n == null)1134                 return null;1135             if (n.value != null)1136                 return n;1137             n.helpDelete(b, n.next);1138         }1139     }1140 1141     /**1142      * Removes first entry; returns its snapshot.1143      * @return null if empty, else snapshot of first entry1144      */1145     Map.Entry<K,V> doRemoveFirstEntry() {1146         for (;;) {1147             Node<K,V> b = head.node;1148             Node<K,V> n = b.next;1149             if (n == null)1150                 return null;1151             Node<K,V> f = n.next;1152             if (n != b.next)1153                 continue;1154             Object v = n.value;1155             if (v == null) {1156                 n.helpDelete(b, f);1157                 continue;1158             }1159             if (!n.casValue(v, null))1160                 continue;1161             if (!n.appendMarker(f) || !b.casNext(n, f))1162                 findFirst(); // retry1163             clearIndexToFirst();1164             return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, (V)v);1165         }1166     }1167 1168     /**1169      * Clears out index nodes associated with deleted first entry.1170      */1171     private void clearIndexToFirst() {1172         for (;;) {1173             Index<K,V> q = head;1174             for (;;) {1175                 Index<K,V> r = q.right;1176                 if (r != null && r.indexesDeletedNode() && !q.unlink(r))1177                     break;1178                 if ((q = q.down) == null) {1179                     if (head.right == null)1180                         tryReduceLevel();1181                     return;1182                 }1183             }1184         }1185     }1186 1187 1188     /* ---------------- Finding and removing last element -------------- */1189 1190     /**1191      * Specialized version of find to get last valid node.1192      * @return last node or null if empty1193      */1194     Node<K,V> findLast() {1195         /*1196          * findPredecessor can't be used to traverse index level1197          * because this doesn't use comparisons.  So traversals of1198          * both levels are folded together.1199          */1200         Index<K,V> q = head;1201         for (;;) {1202             Index<K,V> d, r;1203             if ((r = q.right) != null) {1204                 if (r.indexesDeletedNode()) {1205                     q.unlink(r);1206                     q = head; // restart1207                 }1208                 else1209                     q = r;1210             } else if ((d = q.down) != null) {1211                 q = d;1212             } else {1213                 Node<K,V> b = q.node;1214                 Node<K,V> n = b.next;1215                 for (;;) {1216                     if (n == null)1217                         return b.isBaseHeader() ? null : b;1218                     Node<K,V> f = n.next;            // inconsistent read1219                     if (n != b.next)1220                         break;1221                     Object v = n.value;1222                     if (v == null) {                 // n is deleted1223                         n.helpDelete(b, f);1224                         break;1225                     }1226                     if (v == n || b.value == null)   // b is deleted1227                         break;1228                     b = n;1229                     n = f;1230                 }1231                 q = head; // restart1232             }1233         }1234     }1235 1236     /**1237      * Specialized variant of findPredecessor to get predecessor of last1238      * valid node.  Needed when removing the last entry.  It is possible1239      * that all successors of returned node will have been deleted upon1240      * return, in which case this method can be retried.1241      * @return likely predecessor of last node1242      */1243     private Node<K,V> findPredecessorOfLast() {1244         for (;;) {1245             Index<K,V> q = head;1246             for (;;) {1247                 Index<K,V> d, r;1248                 if ((r = q.right) != null) {1249                     if (r.indexesDeletedNode()) {1250                         q.unlink(r);1251                         break;    // must restart1252                     }1253                     // proceed as far across as possible without overshooting1254                     if (r.node.next != null) {1255                         q = r;1256                         continue;1257                     }1258                 }1259                 if ((d = q.down) != null)1260                     q = d;1261                 else1262                     return q.node;1263             }1264         }1265     }1266 1267     /**1268      * Removes last entry; returns its snapshot.1269      * Specialized variant of doRemove.1270      * @return null if empty, else snapshot of last entry1271      */1272     Map.Entry<K,V> doRemoveLastEntry() {1273         for (;;) {1274             Node<K,V> b = findPredecessorOfLast();1275             Node<K,V> n = b.next;1276             if (n == null) {1277                 if (b.isBaseHeader())               // empty1278                     return null;1279                 else1280                     continue; // all b's successors are deleted; retry1281             }1282             for (;;) {1283                 Node<K,V> f = n.next;1284                 if (n != b.next)                    // inconsistent read1285                     break;1286                 Object v = n.value;1287                 if (v == null) {                    // n is deleted1288                     n.helpDelete(b, f);1289                     break;1290                 }1291                 if (v == n || b.value == null)      // b is deleted1292                     break;1293                 if (f != null) {1294                     b = n;1295                     n = f;1296                     continue;1297                 }1298                 if (!n.casValue(v, null))1299                     break;1300                 K key = n.key;1301                 Comparable<? super K> ck = comparable(key);1302                 if (!n.appendMarker(f) || !b.casNext(n, f))1303                     findNode(ck);                  // Retry via findNode1304                 else {1305                     findPredecessor(ck);           // Clean index1306                     if (head.right == null)1307                         tryReduceLevel();1308                 }1309                 return new AbstractMap.SimpleImmutableEntry<K,V>(key, (V)v);1310             }1311         }1312     }1313 1314     /* ---------------- Relational operations -------------- */1315 1316     // Control values OR'ed as arguments to findNear1317 1318     private static final int EQ = 1;1319     private static final int LT = 2;1320     private static final int GT = 0; // Actually checked as !LT1321 1322     /**1323      * Utility for ceiling, floor, lower, higher methods.1324      * @param kkey the key1325      * @param rel the relation -- OR'ed combination of EQ, LT, GT1326      * @return nearest node fitting relation, or null if no such1327      */1328     Node<K,V> findNear(K kkey, int rel) {1329         Comparable<? super K> key = comparable(kkey);1330         for (;;) {1331             Node<K,V> b = findPredecessor(key);1332             Node<K,V> n = b.next;1333             for (;;) {1334                 if (n == null)1335                     return ((rel & LT) == 0 || b.isBaseHeader()) ? null : b;1336                 Node<K,V> f = n.next;1337                 if (n != b.next)                  // inconsistent read1338                     break;1339                 Object v = n.value;1340                 if (v == null) {                  // n is deleted1341                     n.helpDelete(b, f);1342                     break;1343                 }1344                 if (v == n || b.value == null)    // b is deleted1345                     break;1346                 int c = key.compareTo(n.key);1347                 if ((c == 0 && (rel & EQ) != 0) ||1348                     (c <  0 && (rel & LT) == 0))1349                     return n;1350                 if ( c <= 0 && (rel & LT) != 0)1351                     return b.isBaseHeader() ? null : b;1352                 b = n;1353                 n = f;1354             }1355         }1356     }1357 1358     /**1359      * Returns SimpleImmutableEntry for results of findNear.1360      * @param key the key1361      * @param rel the relation -- OR'ed combination of EQ, LT, GT1362      * @return Entry fitting relation, or null if no such1363      */1364     AbstractMap.SimpleImmutableEntry<K,V> getNear(K key, int rel) {1365         for (;;) {1366             Node<K,V> n = findNear(key, rel);1367             if (n == null)1368                 return null;1369             AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();1370             if (e != null)1371                 return e;1372         }1373     }1374 1375 1376     /* ---------------- Constructors -------------- */1377 1378     /**1379      * Constructs a new, empty map, sorted according to the1380      * {@linkplain Comparable natural ordering} of the keys.1381      */1382     public ConcurrentSkipListMap() {1383         this.comparator = null;1384         initialize();1385     }1386 1387     /**1388      * Constructs a new, empty map, sorted according to the specified1389      * comparator.1390      *1391      * @param comparator the comparator that will be used to order this map.1392      *        If <tt>null</tt>, the {@linkplain Comparable natural1393      *        ordering} of the keys will be used.1394      */1395     public ConcurrentSkipListMap(Comparator<? super K> comparator) {1396         this.comparator = comparator;1397         initialize();1398     }1399 1400     /**1401      * Constructs a new map containing the same mappings as the given map,1402      * sorted according to the {@linkplain Comparable natural ordering} of1403      * the keys.1404      *1405      * @param  m the map whose mappings are to be placed in this map1406      * @throws ClassCastException if the keys in <tt>m</tt> are not1407      *         {@link Comparable}, or are not mutually comparable1408      * @throws NullPointerException if the specified map or any of its keys1409      *         or values are null1410      */1411     public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {1412         this.comparator = null;1413         initialize();1414         putAll(m);1415     }1416 1417     /**1418      * Constructs a new map containing the same mappings and using the1419      * same ordering as the specified sorted map.1420      *1421      * @param m the sorted map whose mappings are to be placed in this1422      *        map, and whose comparator is to be used to sort this map1423      * @throws NullPointerException if the specified sorted map or any of1424      *         its keys or values are null1425      */1426     public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {1427         this.comparator = m.comparator();1428         initialize();1429         buildFromSorted(m);1430     }1431 1432     /**1433      * Returns a shallow copy of this <tt>ConcurrentSkipListMap</tt>1434      * instance. (The keys and values themselves are not cloned.)1435      *1436      * @return a shallow copy of this map1437      */1438     public ConcurrentSkipListMap<K,V> clone() {1439         ConcurrentSkipListMap<K,V> clone = null;1440         try {1441             clone = (ConcurrentSkipListMap<K,V>) super.clone();1442         } catch (CloneNotSupportedException e) {1443             throw new InternalError();1444         }1445 1446         clone.initialize();1447         clone.buildFromSorted(this);1448         return clone;1449     }1450 1451     /**1452      * Streamlined bulk insertion to initialize from elements of1453      * given sorted map.  Call only from constructor or clone1454      * method.1455      */1456     private void buildFromSorted(SortedMap<K, ? extends V> map) {1457         if (map == null)1458             throw new NullPointerException();1459 1460         HeadIndex<K,V> h = head;1461         Node<K,V> basepred = h.node;1462 1463         // Track the current rightmost node at each level. Uses an1464         // ArrayList to avoid committing to initial or maximum level.1465         ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();1466 1467         // initialize1468         for (int i = 0; i <= h.level; ++i)1469             preds.add(null);1470         Index<K,V> q = h;1471         for (int i = h.level; i > 0; --i) {1472             preds.set(i, q);1473             q = q.down;1474         }1475 1476         Iterator<? extends Map.Entry<? extends K, ? extends V>> it =1477             map.entrySet().iterator();1478         while (it.hasNext()) {1479             Map.Entry<? extends K, ? extends V> e = it.next();1480             int j = randomLevel();1481             if (j > h.level) j = h.level + 1;1482             K k = e.getKey();1483             V v = e.getValue();1484             if (k == null || v == null)1485                 throw new NullPointerException();1486             Node<K,V> z = new Node<K,V>(k, v, null);1487             basepred.next = z;1488             basepred = z;1489             if (j > 0) {1490                 Index<K,V> idx = null;1491                 for (int i = 1; i <= j; ++i) {1492                     idx = new Index<K,V>(z, idx, null);1493                     if (i > h.level)1494                         h = new HeadIndex<K,V>(h.node, h, idx, i);1495 1496                     if (i < preds.size()) {1497                         preds.get(i).right = idx;1498                         preds.set(i, idx);1499                     } else1500                         preds.add(idx);1501                 }1502             }1503         }1504         head = h;1505     }1506 1507     /* ---------------- Serialization -------------- */1508 1509     /**1510      * Save the state of this map to a stream.1511      *1512      * @serialData The key (Object) and value (Object) for each1513      * key-value mapping represented by the map, followed by1514      * <tt>null</tt>. The key-value mappings are emitted in key-order1515      * (as determined by the Comparator, or by the keys' natural1516      * ordering if no Comparator).1517      */1518     private void writeObject(java.io.ObjectOutputStream s)1519         throws java.io.IOException {1520         // Write out the Comparator and any hidden stuff1521         s.defaultWriteObject();1522 1523         // Write out keys and values (alternating)1524         for (Node<K,V> n = findFirst(); n != null; n = n.next) {1525             V v = n.getValidValue();1526             if (v != null) {1527                 s.writeObject(n.key);1528                 s.writeObject(v);1529             }1530         }1531         s.writeObject(null);1532     }1533 1534     /**1535      * Reconstitute the map from a stream.1536      */1537     private void readObject(final java.io.ObjectInputStream s)1538         throws java.io.IOException, ClassNotFoundException {1539         // Read in the Comparator and any hidden stuff1540         s.defaultReadObject();1541         // Reset transients1542         initialize();1543 1544         /*1545          * This is nearly identical to buildFromSorted, but is1546          * distinct because readObject calls can't be nicely adapted1547          * as the kind of iterator needed by buildFromSorted. (They1548          * can be, but doing so requires type cheats and/or creation1549          * of adaptor classes.) It is simpler to just adapt the code.1550          */1551 1552         HeadIndex<K,V> h = head;1553         Node<K,V> basepred = h.node;1554         ArrayList<Index<K,V>> preds = new ArrayList<Index<K,V>>();1555         for (int i = 0; i <= h.level; ++i)1556             preds.add(null);1557         Index<K,V> q = h;1558         for (int i = h.level; i > 0; --i) {1559             preds.set(i, q);1560             q = q.down;1561         }1562 1563         for (;;) {1564             Object k = s.readObject();1565             if (k == null)1566                 break;1567             Object v = s.readObject();1568             if (v == null)1569                 throw new NullPointerException();1570             K key = (K) k;1571             V val = (V) v;1572             int j = randomLevel();1573             if (j > h.level) j = h.level + 1;1574             Node<K,V> z = new Node<K,V>(key, val, null);1575             basepred.next = z;1576             basepred = z;1577             if (j > 0) {1578                 Index<K,V> idx = null;1579                 for (int i = 1; i <= j; ++i) {1580                     idx = new Index<K,V>(z, idx, null);1581                     if (i > h.level)1582                         h = new HeadIndex<K,V>(h.node, h, idx, i);1583 1584                     if (i < preds.size()) {1585                         preds.get(i).right = idx;1586                         preds.set(i, idx);1587                     } else1588                         preds.add(idx);1589                 }1590             }1591         }1592         head = h;1593     }1594 1595     /* ------ Map API methods ------ */1596 1597     /**1598      * Returns <tt>true</tt> if this map contains a mapping for the specified1599      * key.1600      *1601      * @param key key whose presence in this map is to be tested1602      * @return <tt>true</tt> if this map contains a mapping for the specified key1603      * @throws ClassCastException if the specified key cannot be compared1604      *         with the keys currently in the map1605      * @throws NullPointerException if the specified key is null1606      */1607     public boolean containsKey(Object key) {1608         return doGet(key) != null;1609     }1610 1611     /**1612      * Returns the value to which the specified key is mapped,1613      * or {@code null} if this map contains no mapping for the key.1614      *1615      * <p>More formally, if this map contains a mapping from a key1616      * {@code k} to a value {@code v} such that {@code key} compares1617      * equal to {@code k} according to the map's ordering, then this1618      * method returns {@code v}; otherwise it returns {@code null}.1619      * (There can be at most one such mapping.)1620      *1621      * @throws ClassCastException if the specified key cannot be compared1622      *         with the keys currently in the map1623      * @throws NullPointerException if the specified key is null1624      */1625     public V get(Object key) {1626         return doGet(key);1627     }1628 1629     /**1630      * Associates the specified value with the specified key in this map.1631      * If the map previously contained a mapping for the key, the old1632      * value is replaced.1633      *1634      * @param key key with which the specified value is to be associated1635      * @param value value to be associated with the specified key1636      * @return the previous value associated with the specified key, or1637      *         <tt>null</tt> if there was no mapping for the key1638      * @throws ClassCastException if the specified key cannot be compared1639      *         with the keys currently in the map1640      * @throws NullPointerException if the specified key or value is null1641      */1642     public V put(K key, V value) {1643         if (value == null)1644             throw new NullPointerException();1645         return doPut(key, value, false);1646     }1647 1648     /**1649      * Removes the mapping for the specified key from this map if present.1650      *1651      * @param  key key for which mapping should be removed1652      * @return the previous value associated with the specified key, or1653      *         <tt>null</tt> if there was no mapping for the key1654      * @throws ClassCastException if the specified key cannot be compared1655      *         with the keys currently in the map1656      * @throws NullPointerException if the specified key is null1657      */1658     public V remove(Object key) {1659         return doRemove(key, null);1660     }1661 1662     /**1663      * Returns <tt>true</tt> if this map maps one or more keys to the1664      * specified value.  This operation requires time linear in the1665      * map size. Additionally, it is possible for the map to change1666      * during execution of this method, in which case the returned1667      * result may be inaccurate.1668      *1669      * @param value value whose presence in this map is to be tested1670      * @return <tt>true</tt> if a mapping to <tt>value</tt> exists;1671      *         <tt>false</tt> otherwise1672      * @throws NullPointerException if the specified value is null1673      */1674     public boolean containsValue(Object value) {1675         if (value == null)1676             throw new NullPointerException();1677         for (Node<K,V> n = findFirst(); n != null; n = n.next) {1678             V v = n.getValidValue();1679             if (v != null && value.equals(v))1680                 return true;1681         }1682         return false;1683     }1684 1685     /**1686      * Returns the number of key-value mappings in this map.  If this map1687      * contains more than <tt>Integer.MAX_VALUE</tt> elements, it1688      * returns <tt>Integer.MAX_VALUE</tt>.1689      *1690      * <p>Beware that, unlike in most collections, this method is1691      * <em>NOT</em> a constant-time operation. Because of the1692      * asynchronous nature of these maps, determining the current1693      * number of elements requires traversing them all to count them.1694      * Additionally, it is possible for the size to change during1695      * execution of this method, in which case the returned result1696      * will be inaccurate. Thus, this method is typically not very1697      * useful in concurrent applications.1698      *1699      * @return the number of elements in this map1700      */1701     public int size() {1702         long count = 0;1703         for (Node<K,V> n = findFirst(); n != null; n = n.next) {1704             if (n.getValidValue() != null)1705                 ++count;1706         }1707         return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;1708     }1709 1710     /**1711      * Returns <tt>true</tt> if this map contains no key-value mappings.1712      * @return <tt>true</tt> if this map contains no key-value mappings1713      */1714     public boolean isEmpty() {1715         return findFirst() == null;1716     }1717 1718     /**1719      * Removes all of the mappings from this map.1720      */1721     public void clear() {1722         initialize();1723     }1724 1725     /* ---------------- View methods -------------- */1726 1727     /*1728      * Note: Lazy initialization works for views because view classes1729      * are stateless/immutable so it doesn't matter wrt correctness if1730      * more than one is created (which will only rarely happen).  Even1731      * so, the following idiom conservatively ensures that the method1732      * returns the one it created if it does so, not one created by1733      * another racing thread.1734      */1735 1736     /**1737      * Returns a {@link NavigableSet} view of the keys contained in this map.1738      * The set's iterator returns the keys in ascending order.1739      * The set is backed by the map, so changes to the map are1740      * reflected in the set, and vice-versa.  The set supports element1741      * removal, which removes the corresponding mapping from the map,1742      * via the {@code Iterator.remove}, {@code Set.remove},1743      * {@code removeAll}, {@code retainAll}, and {@code clear}1744      * operations.  It does not support the {@code add} or {@code addAll}1745      * operations.1746      *1747      * <p>The view's {@code iterator} is a "weakly consistent" iterator1748      * that will never throw {@link ConcurrentModificationException},1749      * and guarantees to traverse elements as they existed upon1750      * construction of the iterator, and may (but is not guaranteed to)1751      * reflect any modifications subsequent to construction.1752      *1753      * <p>This method is equivalent to method {@code navigableKeySet}.1754      *1755      * @return a navigable set view of the keys in this map1756      */1757     public NavigableSet<K> keySet() {1758         KeySet ks = keySet;1759         return (ks != null) ? ks : (keySet = new KeySet(this));1760     }1761 1762     public NavigableSet<K> navigableKeySet() {1763         KeySet ks = keySet;1764         return (ks != null) ? ks : (keySet = new KeySet(this));1765     }1766 1767     /**1768      * Returns a {@link Collection} view of the values contained in this map.1769      * The collection's iterator returns the values in ascending order1770      * of the corresponding keys.1771      * The collection is backed by the map, so changes to the map are1772      * reflected in the collection, and vice-versa.  The collection1773      * supports element removal, which removes the corresponding1774      * mapping from the map, via the <tt>Iterator.remove</tt>,1775      * <tt>Collection.remove</tt>, <tt>removeAll</tt>,1776      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not1777      * support the <tt>add</tt> or <tt>addAll</tt> operations.1778      *1779      * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator1780      * that will never throw {@link ConcurrentModificationException},1781      * and guarantees to traverse elements as they existed upon1782      * construction of the iterator, and may (but is not guaranteed to)1783      * reflect any modifications subsequent to construction.1784      */1785     public Collection<V> values() {1786         Values vs = values;1787         return (vs != null) ? vs : (values = new Values(this));1788     }1789 1790     /**1791      * Returns a {@link Set} view of the mappings contained in this map.1792      * The set's iterator returns the entries in ascending key order.1793      * The set is backed by the map, so changes to the map are1794      * reflected in the set, and vice-versa.  The set supports element1795      * removal, which removes the corresponding mapping from the map,1796      * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,1797      * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt>1798      * operations.  It does not support the <tt>add</tt> or1799      * <tt>addAll</tt> operations.1800      *1801      * <p>The view's <tt>iterator</tt> is a "weakly consistent" iterator1802      * that will never throw {@link ConcurrentModificationException},1803      * and guarantees to traverse elements as they existed upon1804      * construction of the iterator, and may (but is not guaranteed to)1805      * reflect any modifications subsequent to construction.1806      *1807      * <p>The <tt>Map.Entry</tt> elements returned by1808      * <tt>iterator.next()</tt> do <em>not</em> support the1809      * <tt>setValue</tt> operation.1810      *1811      * @return a set view of the mappings contained in this map,1812      *         sorted in ascending key order1813      */1814     public Set<Map.Entry<K,V>> entrySet() {1815         EntrySet es = entrySet;1816         return (es != null) ? es : (entrySet = new EntrySet(this));1817     }1818 1819     public ConcurrentNavigableMap<K,V> descendingMap() {1820         ConcurrentNavigableMap<K,V> dm = descendingMap;1821         return (dm != null) ? dm : (descendingMap = new SubMap<K,V>1822                                     (this, null, false, null, false, true));1823     }1824 1825     public NavigableSet<K> descendingKeySet() {1826         return descendingMap().navigableKeySet();1827     }1828 1829     /* ---------------- AbstractMap Overrides -------------- */1830 1831     /**1832      * Compares the specified object with this map for equality.1833      * Returns <tt>true</tt> if the given object is also a map and the1834      * two maps represent the same mappings.  More formally, two maps1835      * <tt>m1</tt> and <tt>m2</tt> represent the same mappings if1836      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This1837      * operation may return misleading results if either map is1838      * concurrently modified during execution of this method.1839      *1840      * @param o object to be compared for equality with this map1841      * @return <tt>true</tt> if the specified object is equal to this map1842      */1843     public boolean equals(Object o) {1844         if (o == this)1845             return true;1846         if (!(o instanceof Map))1847             return false;1848         Map<?,?> m = (Map<?,?>) o;1849         try {1850             for (Map.Entry<K,V> e : this.entrySet())1851                 if (! e.getValue().equals(m.get(e.getKey())))1852                     return false;1853             for (Map.Entry<?,?> e : m.entrySet()) {1854                 Object k = e.getKey();1855                 Object v = e.getValue();1856                 if (k == null || v == null || !v.equals(get(k)))1857                     return false;1858             }1859             return true;1860         } catch (ClassCastException unused) {1861             return false;1862         } catch (NullPointerException unused) {1863             return false;1864         }1865     }1866 1867     /* ------ ConcurrentMap API methods ------ */1868 1869     /**1870      * {@inheritDoc}1871      *1872      * @return the previous value associated with the specified key,1873      *         or <tt>null</tt> if there was no mapping for the key1874      * @throws ClassCastException if the specified key cannot be compared1875      *         with the keys currently in the map1876      * @throws NullPointerException if the specified key or value is null1877      */1878     public V putIfAbsent(K key, V value) {1879         if (value == null)1880             throw new NullPointerException();1881         return doPut(key, value, true);1882     }1883 1884     /**1885      * {@inheritDoc}1886      *1887      * @throws ClassCastException if the specified key cannot be compared1888      *         with the keys currently in the map1889      * @throws NullPointerException if the specified key is null1890      */1891     public boolean remove(Object key, Object value) {1892         if (key == null)1893             throw new NullPointerException();1894         if (value == null)1895             return false;1896         return doRemove(key, value) != null;1897     }1898 1899     /**1900      * {@inheritDoc}1901      *1902      * @throws ClassCastException if the specified key cannot be compared1903      *         with the keys currently in the map1904      * @throws NullPointerException if any of the arguments are null1905      */1906     public boolean replace(K key, V oldValue, V newValue) {1907         if (oldValue == null || newValue == null)1908             throw new NullPointerException();1909         Comparable<? super K> k = comparable(key);1910         for (;;) {1911             Node<K,V> n = findNode(k);1912             if (n == null)1913                 return false;1914             Object v = n.value;1915             if (v != null) {1916                 if (!oldValue.equals(v))1917                     return false;1918                 if (n.casValue(v, newValue))1919                     return true;1920             }1921         }1922     }1923 1924     /**1925      * {@inheritDoc}1926      *1927      * @return the previous value associated with the specified key,1928      *         or <tt>null</tt> if there was no mapping for the key1929      * @throws ClassCastException if the specified key cannot be compared1930      *         with the keys currently in the map1931      * @throws NullPointerException if the specified key or value is null1932      */1933     public V replace(K key, V value) {1934         if (value == null)1935             throw new NullPointerException();1936         Comparable<? super K> k = comparable(key);1937         for (;;) {1938             Node<K,V> n = findNode(k);1939             if (n == null)1940                 return null;1941             Object v = n.value;1942             if (v != null && n.casValue(v, value))1943                 return (V)v;1944         }1945     }1946 1947     /* ------ SortedMap API methods ------ */1948 1949     public Comparator<? super K> comparator() {1950         return comparator;1951     }1952 1953     /**1954      * @throws NoSuchElementException {@inheritDoc}1955      */1956     public K firstKey() {1957         Node<K,V> n = findFirst();1958         if (n == null)1959             throw new NoSuchElementException();1960         return n.key;1961     }1962 1963     /**1964      * @throws NoSuchElementException {@inheritDoc}1965      */1966     public K lastKey() {1967         Node<K,V> n = findLast();1968         if (n == null)1969             throw new NoSuchElementException();1970         return n.key;1971     }1972 1973     /**1974      * @throws ClassCastException {@inheritDoc}1975      * @throws NullPointerException if {@code fromKey} or {@code toKey} is null1976      * @throws IllegalArgumentException {@inheritDoc}1977      */1978     public ConcurrentNavigableMap<K,V> subMap(K fromKey,1979                                               boolean fromInclusive,1980                                               K toKey,1981                                               boolean toInclusive) {1982         if (fromKey == null || toKey == null)1983             throw new NullPointerException();1984         return new SubMap<K,V>1985             (this, fromKey, fromInclusive, toKey, toInclusive, false);1986     }1987 1988     /**1989      * @throws ClassCastException {@inheritDoc}1990      * @throws NullPointerException if {@code toKey} is null1991      * @throws IllegalArgumentException {@inheritDoc}1992      */1993     public ConcurrentNavigableMap<K,V> headMap(K toKey,1994                                                boolean inclusive) {1995         if (toKey == null)1996             throw new NullPointerException();1997         return new SubMap<K,V>1998             (this, null, false, toKey, inclusive, false);1999     }2000 2001     /**2002      * @throws ClassCastException {@inheritDoc}2003      * @throws NullPointerException if {@code fromKey} is null2004      * @throws IllegalArgumentException {@inheritDoc}2005      */2006     public ConcurrentNavigableMap<K,V> tailMap(K fromKey,2007                                                boolean inclusive) {2008         if (fromKey == null)2009             throw new NullPointerException();2010         return new SubMap<K,V>2011             (this, fromKey, inclusive, null, false, false);2012     }2013 2014     /**2015      * @throws ClassCastException {@inheritDoc}2016      * @throws NullPointerException if {@code fromKey} or {@code toKey} is null2017      * @throws IllegalArgumentException {@inheritDoc}2018      */2019     public ConcurrentNavigableMap<K,V> subMap(K fromKey, K toKey) {2020         return subMap(fromKey, true, toKey, false);2021     }2022 2023     /**2024      * @throws ClassCastException {@inheritDoc}2025      * @throws NullPointerException if {@code toKey} is null2026      * @throws IllegalArgumentException {@inheritDoc}2027      */2028     public ConcurrentNavigableMap<K,V> headMap(K toKey) {2029         return headMap(toKey, false);2030     }2031 2032     /**2033      * @throws ClassCastException {@inheritDoc}2034      * @throws NullPointerException if {@code fromKey} is null2035      * @throws IllegalArgumentException {@inheritDoc}2036      */2037     public ConcurrentNavigableMap<K,V> tailMap(K fromKey) {2038         return tailMap(fromKey, true);2039     }2040 2041     /* ---------------- Relational operations -------------- */2042 2043     /**2044      * Returns a key-value mapping associated with the greatest key2045      * strictly less than the given key, or <tt>null</tt> if there is2046      * no such key. The returned entry does <em>not</em> support the2047      * <tt>Entry.setValue</tt> method.2048      *2049      * @throws ClassCastException {@inheritDoc}2050      * @throws NullPointerException if the specified key is null2051      */2052     public Map.Entry<K,V> lowerEntry(K key) {2053         return getNear(key, LT);2054     }2055 2056     /**2057      * @throws ClassCastException {@inheritDoc}2058      * @throws NullPointerException if the specified key is null2059      */2060     public K lowerKey(K key) {2061         Node<K,V> n = findNear(key, LT);2062         return (n == null) ? null : n.key;2063     }2064 2065     /**2066      * Returns a key-value mapping associated with the greatest key2067      * less than or equal to the given key, or <tt>null</tt> if there2068      * is no such key. The returned entry does <em>not</em> support2069      * the <tt>Entry.setValue</tt> method.2070      *2071      * @param key the key2072      * @throws ClassCastException {@inheritDoc}2073      * @throws NullPointerException if the specified key is null2074      */2075     public Map.Entry<K,V> floorEntry(K key) {2076         return getNear(key, LT|EQ);2077     }2078 2079     /**2080      * @param key the key2081      * @throws ClassCastException {@inheritDoc}2082      * @throws NullPointerException if the specified key is null2083      */2084     public K floorKey(K key) {2085         Node<K,V> n = findNear(key, LT|EQ);2086         return (n == null) ? null : n.key;2087     }2088 2089     /**2090      * Returns a key-value mapping associated with the least key2091      * greater than or equal to the given key, or <tt>null</tt> if2092      * there is no such entry. The returned entry does <em>not</em>2093      * support the <tt>Entry.setValue</tt> method.2094      *2095      * @throws ClassCastException {@inheritDoc}2096      * @throws NullPointerException if the specified key is null2097      */2098     public Map.Entry<K,V> ceilingEntry(K key) {2099         return getNear(key, GT|EQ);2100     }2101 2102     /**2103      * @throws ClassCastException {@inheritDoc}2104      * @throws NullPointerException if the specified key is null2105      */2106     public K ceilingKey(K key) {2107         Node<K,V> n = findNear(key, GT|EQ);2108         return (n == null) ? null : n.key;2109     }2110 2111     /**2112      * Returns a key-value mapping associated with the least key2113      * strictly greater than the given key, or <tt>null</tt> if there2114      * is no such key. The returned entry does <em>not</em> support2115      * the <tt>Entry.setValue</tt> method.2116      *2117      * @param key the key2118      * @throws ClassCastException {@inheritDoc}2119      * @throws NullPointerException if the specified key is null2120      */2121     public Map.Entry<K,V> higherEntry(K key) {2122         return getNear(key, GT);2123     }2124 2125     /**2126      * @param key the key2127      * @throws ClassCastException {@inheritDoc}2128      * @throws NullPointerException if the specified key is null2129      */2130     public K higherKey(K key) {2131         Node<K,V> n = findNear(key, GT);2132         return (n == null) ? null : n.key;2133     }2134 2135     /**2136      * Returns a key-value mapping associated with the least2137      * key in this map, or <tt>null</tt> if the map is empty.2138      * The returned entry does <em>not</em> support2139      * the <tt>Entry.setValue</tt> method.2140      */2141     public Map.Entry<K,V> firstEntry() {2142         for (;;) {2143             Node<K,V> n = findFirst();2144             if (n == null)2145                 return null;2146             AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();2147             if (e != null)2148                 return e;2149         }2150     }2151 2152     /**2153      * Returns a key-value mapping associated with the greatest2154      * key in this map, or <tt>null</tt> if the map is empty.2155      * The returned entry does <em>not</em> support2156      * the <tt>Entry.setValue</tt> method.2157      */2158     public Map.Entry<K,V> lastEntry() {2159         for (;;) {2160             Node<K,V> n = findLast();2161             if (n == null)2162                 return null;2163             AbstractMap.SimpleImmutableEntry<K,V> e = n.createSnapshot();2164             if (e != null)2165                 return e;2166         }2167     }2168 2169     /**2170      * Removes and returns a key-value mapping associated with2171      * the least key in this map, or <tt>null</tt> if the map is empty.2172      * The returned entry does <em>not</em> support2173      * the <tt>Entry.setValue</tt> method.2174      */2175     public Map.Entry<K,V> pollFirstEntry() {2176         return doRemoveFirstEntry();2177     }2178 2179     /**2180      * Removes and returns a key-value mapping associated with2181      * the greatest key in this map, or <tt>null</tt> if the map is empty.2182      * The returned entry does <em>not</em> support2183      * the <tt>Entry.setValue</tt> method.2184      */2185     public Map.Entry<K,V> pollLastEntry() {2186         return doRemoveLastEntry();2187     }2188 2189 2190     /* ---------------- Iterators -------------- */2191 2192     /**2193      * Base of iterator classes:2194      */2195     abstract class Iter<T> implements Iterator<T> {2196         /** the last node returned by next() */2197         Node<K,V> lastReturned;2198         /** the next node to return from next(); */2199         Node<K,V> next;2200         /** Cache of next value field to maintain weak consistency */2201         V nextValue;2202 2203         /** Initializes ascending iterator for entire range. */2204         Iter() {2205             for (;;) {2206                 next = findFirst();2207                 if (next == null)2208                     break;2209                 Object x = next.value;2210                 if (x != null && x != next) {2211                     nextValue = (V) x;2212                     break;2213                 }2214             }2215         }2216 2217         public final boolean hasNext() {2218             return next != null;2219         }2220 2221         /** Advances next to higher entry. */2222         final void advance() {2223             if (next == null)2224                 throw new NoSuchElementException();2225             lastReturned = next;2226             for (;;) {2227                 next = next.next;2228                 if (next == null)2229                     break;2230                 Object x = next.value;2231                 if (x != null && x != next) {2232                     nextValue = (V) x;2233                     break;2234                 }2235             }2236         }2237 2238         public void remove() {2239             Node<K,V> l = lastReturned;2240             if (l == null)2241                 throw new IllegalStateException();2242             // It would not be worth all of the overhead to directly2243             // unlink from here. Using remove is fast enough.2244             ConcurrentSkipListMap.this.remove(l.key);2245             lastReturned = null;2246         }2247 2248     }2249 2250     final class ValueIterator extends Iter<V> {2251         public V next() {2252             V v = nextValue;2253             advance();2254             return v;2255         }2256     }2257 2258     final class KeyIterator extends Iter<K> {2259         public K next() {2260             Node<K,V> n = next;2261             advance();2262             return n.key;2263         }2264     }2265 2266     final class EntryIterator extends Iter<Map.Entry<K,V>> {2267         public Map.Entry<K,V> next() {2268             Node<K,V> n = next;2269             V v = nextValue;2270             advance();2271             return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);2272         }2273     }2274 2275     // Factory methods for iterators needed by ConcurrentSkipListSet etc2276 2277     Iterator<K> keyIterator() {2278         return new KeyIterator();2279     }2280 2281     Iterator<V> valueIterator() {2282         return new ValueIterator();2283     }2284 2285     Iterator<Map.Entry<K,V>> entryIterator() {2286         return new EntryIterator();2287     }2288 2289     /* ---------------- View Classes -------------- */2290 2291     /*2292      * View classes are static, delegating to a ConcurrentNavigableMap2293      * to allow use by SubMaps, which outweighs the ugliness of2294      * needing type-tests for Iterator methods.2295      */2296 2297     static final <E> List<E> toList(Collection<E> c) {2298         // Using size() here would be a pessimization.2299         List<E> list = new ArrayList<E>();2300         for (E e : c)2301             list.add(e);2302         return list;2303     }2304 2305     static final class KeySet<E>2306             extends AbstractSet<E> implements NavigableSet<E> {2307         private final ConcurrentNavigableMap<E,Object> m;2308         KeySet(ConcurrentNavigableMap<E,Object> map) { m = map; }2309         public int size() { return m.size(); }2310         public boolean isEmpty() { return m.isEmpty(); }2311         public boolean contains(Object o) { return m.containsKey(o); }2312         public boolean remove(Object o) { return m.remove(o) != null; }2313         public void clear() { m.clear(); }2314         public E lower(E e) { return m.lowerKey(e); }2315         public E floor(E e) { return m.floorKey(e); }2316         public E ceiling(E e) { return m.ceilingKey(e); }2317         public E higher(E e) { return m.higherKey(e); }2318         public Comparator<? super E> comparator() { return m.comparator(); }2319         public E first() { return m.firstKey(); }2320         public E last() { return m.lastKey(); }2321         public E pollFirst() {2322             Map.Entry<E,Object> e = m.pollFirstEntry();2323             return (e == null) ? null : e.getKey();2324         }2325         public E pollLast() {2326             Map.Entry<E,Object> e = m.pollLastEntry();2327             return (e == null) ? null : e.getKey();2328         }2329         public Iterator<E> iterator() {2330             if (m instanceof ConcurrentSkipListMap)2331                 return ((ConcurrentSkipListMap<E,Object>)m).keyIterator();2332             else2333                 return ((ConcurrentSkipListMap.SubMap<E,Object>)m).keyIterator();2334         }2335         public boolean equals(Object o) {2336             if (o == this)2337                 return true;2338             if (!(o instanceof Set))2339                 return false;2340             Collection<?> c = (Collection<?>) o;2341             try {2342                 return containsAll(c) && c.containsAll(this);2343             } catch (ClassCastException unused)   {2344                 return false;2345             } catch (NullPointerException unused) {2346                 return false;2347             }2348         }2349         public Object[] toArray()     { return toList(this).toArray();  }2350         public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }2351         public Iterator<E> descendingIterator() {2352             return descendingSet().iterator();2353         }2354         public NavigableSet<E> subSet(E fromElement,2355                                       boolean fromInclusive,2356                                       E toElement,2357                                       boolean toInclusive) {2358             return new KeySet<E>(m.subMap(fromElement, fromInclusive,2359                                           toElement,   toInclusive));2360         }2361         public NavigableSet<E> headSet(E toElement, boolean inclusive) {2362             return new KeySet<E>(m.headMap(toElement, inclusive));2363         }2364         public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {2365             return new KeySet<E>(m.tailMap(fromElement, inclusive));2366         }2367         public NavigableSet<E> subSet(E fromElement, E toElement) {2368             return subSet(fromElement, true, toElement, false);2369         }2370         public NavigableSet<E> headSet(E toElement) {2371             return headSet(toElement, false);2372         }2373         public NavigableSet<E> tailSet(E fromElement) {2374             return tailSet(fromElement, true);2375         }2376         public NavigableSet<E> descendingSet() {2377             return new KeySet(m.descendingMap());2378         }2379     }2380 2381     static final class Values<E> extends AbstractCollection<E> {2382         private final ConcurrentNavigableMap<Object, E> m;2383         Values(ConcurrentNavigableMap<Object, E> map) {2384             m = map;2385         }2386         public Iterator<E> iterator() {2387             if (m instanceof ConcurrentSkipListMap)2388                 return ((ConcurrentSkipListMap<Object,E>)m).valueIterator();2389             else2390                 return ((SubMap<Object,E>)m).valueIterator();2391         }2392         public boolean isEmpty() {2393             return m.isEmpty();2394         }2395         public int size() {2396             return m.size();2397         }2398         public boolean contains(Object o) {2399             return m.containsValue(o);2400         }2401         public void clear() {2402             m.clear();2403         }2404         public Object[] toArray()     { return toList(this).toArray();  }2405         public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }2406     }2407 2408     static final class EntrySet<K1,V1> extends AbstractSet<Map.Entry<K1,V1>> {2409         private final ConcurrentNavigableMap<K1, V1> m;2410         EntrySet(ConcurrentNavigableMap<K1, V1> map) {2411             m = map;2412         }2413 2414         public Iterator<Map.Entry<K1,V1>> iterator() {2415             if (m instanceof ConcurrentSkipListMap)2416                 return ((ConcurrentSkipListMap<K1,V1>)m).entryIterator();2417             else2418                 return ((SubMap<K1,V1>)m).entryIterator();2419         }2420 2421         public boolean contains(Object o) {2422             if (!(o instanceof Map.Entry))2423                 return false;2424             Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;2425             V1 v = m.get(e.getKey());2426             return v != null && v.equals(e.getValue());2427         }2428         public boolean remove(Object o) {2429             if (!(o instanceof Map.Entry))2430                 return false;2431             Map.Entry<K1,V1> e = (Map.Entry<K1,V1>)o;2432             return m.remove(e.getKey(),2433                             e.getValue());2434         }2435         public boolean isEmpty() {2436             return m.isEmpty();2437         }2438         public int size() {2439             return m.size();2440         }2441         public void clear() {2442             m.clear();2443         }2444         public boolean equals(Object o) {2445             if (o == this)2446                 return true;2447             if (!(o instanceof Set))2448                 return false;2449             Collection<?> c = (Collection<?>) o;2450             try {2451                 return containsAll(c) && c.containsAll(this);2452             } catch (ClassCastException unused)   {2453                 return false;2454             } catch (NullPointerException unused) {2455                 return false;2456             }2457         }2458         public Object[] toArray()     { return toList(this).toArray();  }2459         public <T> T[] toArray(T[] a) { return toList(this).toArray(a); }2460     }2461 2462     /**2463      * Submaps returned by {@link ConcurrentSkipListMap} submap operations2464      * represent a subrange of mappings of their underlying2465      * maps. Instances of this class support all methods of their2466      * underlying maps, differing in that mappings outside their range are2467      * ignored, and attempts to add mappings outside their ranges result2468      * in {@link IllegalArgumentException}.  Instances of this class are2469      * constructed only using the <tt>subMap</tt>, <tt>headMap</tt>, and2470      * <tt>tailMap</tt> methods of their underlying maps.2471      *2472      * @serial include2473      */2474     static final class SubMap<K,V> extends AbstractMap<K,V>2475         implements ConcurrentNavigableMap<K,V>, Cloneable,2476                    java.io.Serializable {2477         private static final long serialVersionUID = -7647078645895051609L;2478 2479         /** Underlying map */2480         private final ConcurrentSkipListMap<K,V> m;2481         /** lower bound key, or null if from start */2482         private final K lo;2483         /** upper bound key, or null if to end */2484         private final K hi;2485         /** inclusion flag for lo */2486         private final boolean loInclusive;2487         /** inclusion flag for hi */2488         private final boolean hiInclusive;2489         /** direction */2490         private final boolean isDescending;2491 2492         // Lazily initialized view holders2493         private transient KeySet<K> keySetView;2494         private transient Set<Map.Entry<K,V>> entrySetView;2495         private transient Collection<V> valuesView;2496 2497         /**2498          * Creates a new submap, initializing all fields2499          */2500         SubMap(ConcurrentSkipListMap<K,V> map,2501                K fromKey, boolean fromInclusive,2502                K toKey, boolean toInclusive,2503                boolean isDescending) {2504             if (fromKey != null && toKey != null &&2505                 map.compare(fromKey, toKey) > 0)2506                 throw new IllegalArgumentException("inconsistent range");2507             this.m = map;2508             this.lo = fromKey;2509             this.hi = toKey;2510             this.loInclusive = fromInclusive;2511             this.hiInclusive = toInclusive;2512             this.isDescending = isDescending;2513         }2514 2515         /* ----------------  Utilities -------------- */2516 2517         private boolean tooLow(K key) {2518             if (lo != null) {2519                 int c = m.compare(key, lo);2520                 if (c < 0 || (c == 0 && !loInclusive))2521                     return true;2522             }2523             return false;2524         }2525 2526         private boolean tooHigh(K key) {2527             if (hi != null) {2528                 int c = m.compare(key, hi);2529                 if (c > 0 || (c == 0 && !hiInclusive))2530                     return true;2531             }2532             return false;2533         }2534 2535         private boolean inBounds(K key) {2536             return !tooLow(key) && !tooHigh(key);2537         }2538 2539         private void checkKeyBounds(K key) throws IllegalArgumentException {2540             if (key == null)2541                 throw new NullPointerException();2542             if (!inBounds(key))2543                 throw new IllegalArgumentException("key out of range");2544         }2545 2546         /**2547          * Returns true if node key is less than upper bound of range2548          */2549         private boolean isBeforeEnd(ConcurrentSkipListMap.Node<K,V> n) {2550             if (n == null)2551                 return false;2552             if (hi == null)2553                 return true;2554             K k = n.key;2555             if (k == null) // pass by markers and headers2556                 return true;2557             int c = m.compare(k, hi);2558             if (c > 0 || (c == 0 && !hiInclusive))2559                 return false;2560             return true;2561         }2562 2563         /**2564          * Returns lowest node. This node might not be in range, so2565          * most usages need to check bounds2566          */2567         private ConcurrentSkipListMap.Node<K,V> loNode() {2568             if (lo == null)2569                 return m.findFirst();2570             else if (loInclusive)2571                 return m.findNear(lo, m.GT|m.EQ);2572             else2573                 return m.findNear(lo, m.GT);2574         }2575 2576         /**2577          * Returns highest node. This node might not be in range, so2578          * most usages need to check bounds2579          */2580         private ConcurrentSkipListMap.Node<K,V> hiNode() {2581             if (hi == null)2582                 return m.findLast();2583             else if (hiInclusive)2584                 return m.findNear(hi, m.LT|m.EQ);2585             else2586                 return m.findNear(hi, m.LT);2587         }2588 2589         /**2590          * Returns lowest absolute key (ignoring directonality)2591          */2592         private K lowestKey() {2593             ConcurrentSkipListMap.Node<K,V> n = loNode();2594             if (isBeforeEnd(n))2595                 return n.key;2596             else2597                 throw new NoSuchElementException();2598         }2599 2600         /**2601          * Returns highest absolute key (ignoring directonality)2602          */2603         private K highestKey() {2604             ConcurrentSkipListMap.Node<K,V> n = hiNode();2605             if (n != null) {2606                 K last = n.key;2607                 if (inBounds(last))2608                     return last;2609             }2610             throw new NoSuchElementException();2611         }2612 2613         private Map.Entry<K,V> lowestEntry() {2614             for (;;) {2615                 ConcurrentSkipListMap.Node<K,V> n = loNode();2616                 if (!isBeforeEnd(n))2617                     return null;2618                 Map.Entry<K,V> e = n.createSnapshot();2619                 if (e != null)2620                     return e;2621             }2622         }2623 2624         private Map.Entry<K,V> highestEntry() {2625             for (;;) {2626                 ConcurrentSkipListMap.Node<K,V> n = hiNode();2627                 if (n == null || !inBounds(n.key))2628                     return null;2629                 Map.Entry<K,V> e = n.createSnapshot();2630                 if (e != null)2631                     return e;2632             }2633         }2634 2635         private Map.Entry<K,V> removeLowest() {2636             for (;;) {2637                 Node<K,V> n = loNode();2638                 if (n == null)2639                     return null;2640                 K k = n.key;2641                 if (!inBounds(k))2642                     return null;2643                 V v = m.doRemove(k, null);2644                 if (v != null)2645                     return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);2646             }2647         }2648 2649         private Map.Entry<K,V> removeHighest() {2650             for (;;) {2651                 Node<K,V> n = hiNode();2652                 if (n == null)2653                     return null;2654                 K k = n.key;2655                 if (!inBounds(k))2656                     return null;2657                 V v = m.doRemove(k, null);2658                 if (v != null)2659                     return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);2660             }2661         }2662 2663         /**2664          * Submap version of ConcurrentSkipListMap.getNearEntry2665          */2666         private Map.Entry<K,V> getNearEntry(K key, int rel) {2667             if (isDescending) { // adjust relation for direction2668                 if ((rel & m.LT) == 0)2669                     rel |= m.LT;2670                 else2671                     rel &= ~m.LT;2672             }2673             if (tooLow(key))2674                 return ((rel & m.LT) != 0) ? null : lowestEntry();2675             if (tooHigh(key))2676                 return ((rel & m.LT) != 0) ? highestEntry() : null;2677             for (;;) {2678                 Node<K,V> n = m.findNear(key, rel);2679                 if (n == null || !inBounds(n.key))2680                     return null;2681                 K k = n.key;2682                 V v = n.getValidValue();2683                 if (v != null)2684                     return new AbstractMap.SimpleImmutableEntry<K,V>(k, v);2685             }2686         }2687 2688         // Almost the same as getNearEntry, except for keys2689         private K getNearKey(K key, int rel) {2690             if (isDescending) { // adjust relation for direction2691                 if ((rel & m.LT) == 0)2692                     rel |= m.LT;2693                 else2694                     rel &= ~m.LT;2695             }2696             if (tooLow(key)) {2697                 if ((rel & m.LT) == 0) {2698                     ConcurrentSkipListMap.Node<K,V> n = loNode();2699                     if (isBeforeEnd(n))2700                         return n.key;2701                 }2702                 return null;2703             }2704             if (tooHigh(key)) {2705                 if ((rel & m.LT) != 0) {2706                     ConcurrentSkipListMap.Node<K,V> n = hiNode();2707                     if (n != null) {2708                         K last = n.key;2709                         if (inBounds(last))2710                             return last;2711                     }2712                 }2713                 return null;2714             }2715             for (;;) {2716                 Node<K,V> n = m.findNear(key, rel);2717                 if (n == null || !inBounds(n.key))2718                     return null;2719                 K k = n.key;2720                 V v = n.getValidValue();2721                 if (v != null)2722                     return k;2723             }2724         }2725 2726         /* ----------------  Map API methods -------------- */2727 2728         public boolean containsKey(Object key) {2729             if (key == null) throw new NullPointerException();2730             K k = (K)key;2731             return inBounds(k) && m.containsKey(k);2732         }2733 2734         public V get(Object key) {2735             if (key == null) throw new NullPointerException();2736             K k = (K)key;2737             return ((!inBounds(k)) ? null : m.get(k));2738         }2739 2740         public V put(K key, V value) {2741             checkKeyBounds(key);2742             return m.put(key, value);2743         }2744 2745         public V remove(Object key) {2746             K k = (K)key;2747             return (!inBounds(k)) ? null : m.remove(k);2748         }2749 2750         public int size() {2751             long count = 0;2752             for (ConcurrentSkipListMap.Node<K,V> n = loNode();2753                  isBeforeEnd(n);2754                  n = n.next) {2755                 if (n.getValidValue() != null)2756                     ++count;2757             }2758             return count >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)count;2759         }2760 2761         public boolean isEmpty() {2762             return !isBeforeEnd(loNode());2763         }2764 2765         public boolean containsValue(Object value) {2766             if (value == null)2767                 throw new NullPointerException();2768             for (ConcurrentSkipListMap.Node<K,V> n = loNode();2769                  isBeforeEnd(n);2770                  n = n.next) {2771                 V v = n.getValidValue();2772                 if (v != null && value.equals(v))2773                     return true;2774             }2775             return false;2776         }2777 2778         public void clear() {2779             for (ConcurrentSkipListMap.Node<K,V> n = loNode();2780                  isBeforeEnd(n);2781                  n = n.next) {2782                 if (n.getValidValue() != null)2783                     m.remove(n.key);2784             }2785         }2786 2787         /* ----------------  ConcurrentMap API methods -------------- */2788 2789         public V putIfAbsent(K key, V value) {2790             checkKeyBounds(key);2791             return m.putIfAbsent(key, value);2792         }2793 2794         public boolean remove(Object key, Object value) {2795             K k = (K)key;2796             return inBounds(k) && m.remove(k, value);2797         }2798 2799         public boolean replace(K key, V oldValue, V newValue) {2800             checkKeyBounds(key);2801             return m.replace(key, oldValue, newValue);2802         }2803 2804         public V replace(K key, V value) {2805             checkKeyBounds(key);2806             return m.replace(key, value);2807         }2808 2809         /* ----------------  SortedMap API methods -------------- */2810 2811         public Comparator<? super K> comparator() {2812             Comparator<? super K> cmp = m.comparator();2813             if (isDescending)2814                 return Collections.reverseOrder(cmp);2815             else2816                 return cmp;2817         }2818 2819         /**2820          * Utility to create submaps, where given bounds override2821          * unbounded(null) ones and/or are checked against bounded ones.2822          */2823         private SubMap<K,V> newSubMap(K fromKey,2824                                       boolean fromInclusive,2825                                       K toKey,2826                                       boolean toInclusive) {2827             if (isDescending) { // flip senses2828                 K tk = fromKey;2829                 fromKey = toKey;2830                 toKey = tk;2831                 boolean ti = fromInclusive;2832                 fromInclusive = toInclusive;2833                 toInclusive = ti;2834             }2835             if (lo != null) {2836                 if (fromKey == null) {2837                     fromKey = lo;2838                     fromInclusive = loInclusive;2839                 }2840                 else {2841                     int c = m.compare(fromKey, lo);2842                     if (c < 0 || (c == 0 && !loInclusive && fromInclusive))2843                         throw new IllegalArgumentException("key out of range");2844                 }2845             }2846             if (hi != null) {2847                 if (toKey == null) {2848                     toKey = hi;2849                     toInclusive = hiInclusive;2850                 }2851                 else {2852                     int c = m.compare(toKey, hi);2853                     if (c > 0 || (c == 0 && !hiInclusive && toInclusive))2854                         throw new IllegalArgumentException("key out of range");2855                 }2856             }2857             return new SubMap<K,V>(m, fromKey, fromInclusive,2858                                    toKey, toInclusive, isDescending);2859         }2860 2861         public SubMap<K,V> subMap(K fromKey,2862                                   boolean fromInclusive,2863                                   K toKey,2864                                   boolean toInclusive) {2865             if (fromKey == null || toKey == null)2866                 throw new NullPointerException();2867             return newSubMap(fromKey, fromInclusive, toKey, toInclusive);2868         }2869 2870         public SubMap<K,V> headMap(K toKey,2871                                    boolean inclusive) {2872             if (toKey == null)2873                 throw new NullPointerException();2874             return newSubMap(null, false, toKey, inclusive);2875         }2876 2877         public SubMap<K,V> tailMap(K fromKey,2878                                    boolean inclusive) {2879             if (fromKey == null)2880                 throw new NullPointerException();2881             return newSubMap(fromKey, inclusive, null, false);2882         }2883 2884         public SubMap<K,V> subMap(K fromKey, K toKey) {2885             return subMap(fromKey, true, toKey, false);2886         }2887 2888         public SubMap<K,V> headMap(K toKey) {2889             return headMap(toKey, false);2890         }2891 2892         public SubMap<K,V> tailMap(K fromKey) {2893             return tailMap(fromKey, true);2894         }2895 2896         public SubMap<K,V> descendingMap() {2897             return new SubMap<K,V>(m, lo, loInclusive,2898                                    hi, hiInclusive, !isDescending);2899         }2900 2901         /* ----------------  Relational methods -------------- */2902 2903         public Map.Entry<K,V> ceilingEntry(K key) {2904             return getNearEntry(key, (m.GT|m.EQ));2905         }2906 2907         public K ceilingKey(K key) {2908             return getNearKey(key, (m.GT|m.EQ));2909         }2910 2911         public Map.Entry<K,V> lowerEntry(K key) {2912             return getNearEntry(key, (m.LT));2913         }2914 2915         public K lowerKey(K key) {2916             return getNearKey(key, (m.LT));2917         }2918 2919         public Map.Entry<K,V> floorEntry(K key) {2920             return getNearEntry(key, (m.LT|m.EQ));2921         }2922 2923         public K floorKey(K key) {2924             return getNearKey(key, (m.LT|m.EQ));2925         }2926 2927         public Map.Entry<K,V> higherEntry(K key) {2928             return getNearEntry(key, (m.GT));2929         }2930 2931         public K higherKey(K key) {2932             return getNearKey(key, (m.GT));2933         }2934 2935         public K firstKey() {2936             return isDescending ? highestKey() : lowestKey();2937         }2938 2939         public K lastKey() {2940             return isDescending ? lowestKey() : highestKey();2941         }2942 2943         public Map.Entry<K,V> firstEntry() {2944             return isDescending ? highestEntry() : lowestEntry();2945         }2946 2947         public Map.Entry<K,V> lastEntry() {2948             return isDescending ? lowestEntry() : highestEntry();2949         }2950 2951         public Map.Entry<K,V> pollFirstEntry() {2952             return isDescending ? removeHighest() : removeLowest();2953         }2954 2955         public Map.Entry<K,V> pollLastEntry() {2956             return isDescending ? removeLowest() : removeHighest();2957         }2958 2959         /* ---------------- Submap Views -------------- */2960 2961         public NavigableSet<K> keySet() {2962             KeySet<K> ks = keySetView;2963             return (ks != null) ? ks : (keySetView = new KeySet(this));2964         }2965 2966         public NavigableSet<K> navigableKeySet() {2967             KeySet<K> ks = keySetView;2968             return (ks != null) ? ks : (keySetView = new KeySet(this));2969         }2970 2971         public Collection<V> values() {2972             Collection<V> vs = valuesView;2973             return (vs != null) ? vs : (valuesView = new Values(this));2974         }2975 2976         public Set<Map.Entry<K,V>> entrySet() {2977             Set<Map.Entry<K,V>> es = entrySetView;2978             return (es != null) ? es : (entrySetView = new EntrySet(this));2979         }2980 2981         public NavigableSet<K> descendingKeySet() {2982             return descendingMap().navigableKeySet();2983         }2984 2985         Iterator<K> keyIterator() {2986             return new SubMapKeyIterator();2987         }2988 2989         Iterator<V> valueIterator() {2990             return new SubMapValueIterator();2991         }2992 2993         Iterator<Map.Entry<K,V>> entryIterator() {2994             return new SubMapEntryIterator();2995         }2996 2997         /**2998          * Variant of main Iter class to traverse through submaps.2999          */3000         abstract class SubMapIter<T> implements Iterator<T> {3001             /** the last node returned by next() */3002             Node<K,V> lastReturned;3003             /** the next node to return from next(); */3004             Node<K,V> next;3005             /** Cache of next value field to maintain weak consistency */3006             V nextValue;3007 3008             SubMapIter() {3009                 for (;;) {3010                     next = isDescending ? hiNode() : loNode();3011                     if (next == null)3012                         break;3013                     Object x = next.value;3014                     if (x != null && x != next) {3015                         if (! inBounds(next.key))3016                             next = null;3017                         else3018                             nextValue = (V) x;3019                         break;3020                     }3021                 }3022             }3023 3024             public final boolean hasNext() {3025                 return next != null;3026             }3027 3028             final void advance() {3029                 if (next == null)3030                     throw new NoSuchElementException();3031                 lastReturned = next;3032                 if (isDescending)3033                     descend();3034                 else3035                     ascend();3036             }3037 3038             private void ascend() {3039                 for (;;) {3040                     next = next.next;3041                     if (next == null)3042                         break;3043                     Object x = next.value;3044                     if (x != null && x != next) {3045                         if (tooHigh(next.key))3046                             next = null;3047                         else3048                             nextValue = (V) x;3049                         break;3050                     }3051                 }3052             }3053 3054             private void descend() {3055                 for (;;) {3056                     next = m.findNear(lastReturned.key, LT);3057                     if (next == null)3058                         break;3059                     Object x = next.value;3060                     if (x != null && x != next) {3061                         if (tooLow(next.key))3062                             next = null;3063                         else3064                             nextValue = (V) x;3065                         break;3066                     }3067                 }3068             }3069 3070             public void remove() {3071                 Node<K,V> l = lastReturned;3072                 if (l == null)3073                     throw new IllegalStateException();3074                 m.remove(l.key);3075                 lastReturned = null;3076             }3077 3078         }3079 3080         final class SubMapValueIterator extends SubMapIter<V> {3081             public V next() {3082                 V v = nextValue;3083                 advance();3084                 return v;3085             }3086         }3087 3088         final class SubMapKeyIterator extends SubMapIter<K> {3089             public K next() {3090                 Node<K,V> n = next;3091                 advance();3092                 return n.key;3093             }3094         }3095 3096         final class SubMapEntryIterator extends SubMapIter<Map.Entry<K,V>> {3097             public Map.Entry<K,V> next() {3098                 Node<K,V> n = next;3099                 V v = nextValue;3100                 advance();3101                 return new AbstractMap.SimpleImmutableEntry<K,V>(n.key, v);3102             }3103         }3104     }3105 3106     // Unsafe mechanics3107     private static final sun.misc.Unsafe UNSAFE;3108     private static final long headOffset;3109     static {3110         try {3111             UNSAFE = sun.misc.Unsafe.getUnsafe();3112             Class k = ConcurrentSkipListMap.class;3113             headOffset = UNSAFE.objectFieldOffset3114                 (k.getDeclaredField("head"));3115         } catch (Exception e) {3116             throw new Error(e);3117         }3118     }3119 }
复制代码

 

下面从ConcurrentSkipListMap的添加,删除,获取这3个方面对它进行分析。

1. 添加

下面以put(K key, V value)为例,对ConcurrentSkipListMap的添加方法进行说明。

public V put(K key, V value) {    if (value == null)        throw new NullPointerException();    return doPut(key, value, false);}

实际上,put()是通过doPut()将key-value键值对添加到ConcurrentSkipListMap中的。

doPut()的源码如下:

复制代码
private V doPut(K kkey, V value, boolean onlyIfAbsent) {    Comparable<? super K> key = comparable(kkey);    for (;;) {        // 找到key的前继节点        Node<K,V> b = findPredecessor(key);        // 设置n为“key的前继节点的后继节点”,即n应该是“插入节点”的“后继节点”        Node<K,V> n = b.next;        for (;;) {            if (n != null) {                Node<K,V> f = n.next;                // 如果两次获得的b.next不是相同的Node,就跳转到”外层for循环“,重新获得b和n后再遍历。                if (n != b.next)                    break;                // v是“n的值”                Object v = n.value;                // 当n的值为null(意味着其它线程删除了n);此时删除b的下一个节点,然后跳转到”外层for循环“,重新获得b和n后再遍历。                if (v == null) {               // n is deleted                    n.helpDelete(b, f);                    break;                }                // 如果其它线程删除了b;则跳转到”外层for循环“,重新获得b和n后再遍历。                if (v == n || b.value == null) // b is deleted                    break;                // 比较key和n.key                int c = key.compareTo(n.key);                if (c > 0) {                    b = n;                    n = f;                    continue;                }                if (c == 0) {                    if (onlyIfAbsent || n.casValue(v, value))                        return (V)v;                    else                        break; // restart if lost race to replace value                }                // else c < 0; fall through            }            // 新建节点(对应是“要插入的键值对”)            Node<K,V> z = new Node<K,V>(kkey, value, n);            // 设置“b的后继节点”为z            if (!b.casNext(n, z))                break;         // 多线程情况下,break才可能发生(其它线程对b进行了操作)            // 随机获取一个level            // 然后在“第1层”到“第level层”的链表中都插入新建节点            int level = randomLevel();            if (level > 0)                insertIndex(z, level);            return null;        }    }}
复制代码

说明:doPut() 的作用就是将键值对添加到“跳表”中。
要想搞清doPut(),首先要弄清楚它的主干部分 —— 我们先单纯的只考虑“单线程的情况下,将key-value添加到跳表中”,即忽略“多线程相关的内容”。它的流程如下:
第1步:找到“插入位置”。
即,找到“key的前继节点(b)”和“key的后继节点(n)”;key是要插入节点的键。
第2步:新建并插入节点。
即,新建节点z(key对应的节点),并将新节点z插入到“跳表”中(设置“b的后继节点为z”,“z的后继节点为n”)。
第3步:更新跳表。
即,随机获取一个level,然后在“跳表”的第1层~第level层之间,每一层都插入节点z;在第level层之上就不再插入节点了。若level数值大于“跳表的层次”,则新建一层。
主干部分“对应的精简后的doPut()的代码”如下(仅供参考):

复制代码
private V doPut(K kkey, V value, boolean onlyIfAbsent) {    Comparable<? super K> key = comparable(kkey);    for (;;) {        // 找到key的前继节点        Node<K,V> b = findPredecessor(key);        // 设置n为key的后继节点        Node<K,V> n = b.next;        for (;;) {                        // 新建节点(对应是“要被插入的键值对”)            Node<K,V> z = new Node<K,V>(kkey, value, n);            // 设置“b的后继节点”为z            b.casNext(n, z);            // 随机获取一个level            // 然后在“第1层”到“第level层”的链表中都插入新建节点            int level = randomLevel();            if (level > 0)                insertIndex(z, level);            return null;        }    }}
复制代码

理清主干之后,剩余的工作就相对简单了。主要是上面几步的对应算法的具体实现,以及多线程相关情况的处理!

 

2. 删除

下面以remove(Object key)为例,对ConcurrentSkipListMap的删除方法进行说明。

public V remove(Object key) {    return doRemove(key, null);}

实际上,remove()是通过doRemove()将ConcurrentSkipListMap中的key对应的键值对删除的。

doRemove()的源码如下:

 

复制代码
final V doRemove(Object okey, Object value) {    Comparable<? super K> key = comparable(okey);    for (;;) {        // 找到“key的前继节点”        Node<K,V> b = findPredecessor(key);        // 设置n为“b的后继节点”(即若key存在于“跳表中”,n就是key对应的节点)        Node<K,V> n = b.next;        for (;;) {            if (n == null)                return null;            // f是“当前节点n的后继节点”            Node<K,V> f = n.next;            // 如果两次读取到的“b的后继节点”不同(其它线程操作了该跳表),则返回到“外层for循环”重新遍历。            if (n != b.next)                    // inconsistent read                break;            // 如果“当前节点n的值”变为null(其它线程操作了该跳表),则返回到“外层for循环”重新遍历。            Object v = n.value;            if (v == null) {                    // n is deleted                n.helpDelete(b, f);                break;            }            // 如果“前继节点b”被删除(其它线程操作了该跳表),则返回到“外层for循环”重新遍历。            if (v == n || b.value == null)      // b is deleted                break;            int c = key.compareTo(n.key);            if (c < 0)                return null;            if (c > 0) {                b = n;                n = f;                continue;            }            // 以下是c=0的情况            if (value != null && !value.equals(v))                return null;            // 设置“当前节点n”的值为null            if (!n.casValue(v, null))                break;            // 设置“b的后继节点”为f            if (!n.appendMarker(f) || !b.casNext(n, f))                findNode(key);                  // Retry via findNode            else {                // 清除“跳表”中每一层的key节点                findPredecessor(key);           // Clean index                // 如果“表头的右索引为空”,则将“跳表的层次”-1。                if (head.right == null)                    tryReduceLevel();            }            return (V)v;        }    }}
复制代码

说明:doRemove()的作用是删除跳表中的节点。
和doPut()一样,我们重点看doRemove()的主干部分,了解主干部分之后,其余部分就非常容易理解了。下面是“单线程的情况下,删除跳表中键值对的步骤”:
第1步:找到“被删除节点的位置”。
即,找到“key的前继节点(b)”,“key所对应的节点(n)”,“n的后继节点f”;key是要删除节点的键。
第2步:删除节点。
即,将“key所对应的节点n”从跳表中移除 -- 将“b的后继节点”设为“f”!
第3步:更新跳表。
即,遍历跳表,删除每一层的“key节点”(如果存在的话)。如果删除“key节点”之后,跳表的层次需要-1;则执行相应的操作!
主干部分“对应的精简后的doRemove()的代码”如下(仅供参考):

 

复制代码
final V doRemove(Object okey, Object value) {    Comparable<? super K> key = comparable(okey);    for (;;) {        // 找到“key的前继节点”        Node<K,V> b = findPredecessor(key);        // 设置n为“b的后继节点”(即若key存在于“跳表中”,n就是key对应的节点)        Node<K,V> n = b.next;        for (;;) {            // f是“当前节点n的后继节点”            Node<K,V> f = n.next;            // 设置“当前节点n”的值为null            n.casValue(v, null);            // 设置“b的后继节点”为f            b.casNext(n, f);            // 清除“跳表”中每一层的key节点            findPredecessor(key);            // 如果“表头的右索引为空”,则将“跳表的层次”-1。            if (head.right == null)                tryReduceLevel();            return (V)v;        }    }}
复制代码

 

3. 获取

下面以get(Object key)为例,对ConcurrentSkipListMap的获取方法进行说明。

public V get(Object key) {    return doGet(key);}

doGet的源码如下:

复制代码
private V doGet(Object okey) {    Comparable<? super K> key = comparable(okey);    for (;;) {        // 找到“key对应的节点”        Node<K,V> n = findNode(key);        if (n == null)            return null;        Object v = n.value;        if (v != null)            return (V)v;    }}
复制代码

说明:doGet()是通过findNode()找到并返回节点的。

复制代码
private Node<K,V> findNode(Comparable<? super K> key) {    for (;;) {        // 找到key的前继节点        Node<K,V> b = findPredecessor(key);        // 设置n为“b的后继节点”(即若key存在于“跳表中”,n就是key对应的节点)        Node<K,V> n = b.next;        for (;;) {            // 如果“n为null”,则跳转中不存在key对应的节点,直接返回null。            if (n == null)                return null;            Node<K,V> f = n.next;            // 如果两次读取到的“b的后继节点”不同(其它线程操作了该跳表),则返回到“外层for循环”重新遍历。            if (n != b.next)                // inconsistent read                break;            Object v = n.value;            // 如果“当前节点n的值”变为null(其它线程操作了该跳表),则返回到“外层for循环”重新遍历。            if (v == null) {                // n is deleted                n.helpDelete(b, f);                break;            }            if (v == n || b.value == null)  // b is deleted                break;            // 若n是当前节点,则返回n。            int c = key.compareTo(n.key);            if (c == 0)                return n;            // 若“节点n的key”小于“key”,则说明跳表中不存在key对应的节点,返回null            if (c < 0)                return null;            // 若“节点n的key”大于“key”,则更新b和n,继续查找。            b = n;            n = f;        }    }}
复制代码

说明:findNode(key)的作用是在返回跳表中key对应的节点;存在则返回节点,不存在则返回null。
先弄清函数的主干部分,即抛开“多线程相关内容”,单纯的考虑单线程情况下,从跳表获取节点的算法。
第1步:找到“被删除节点的位置”。
根据findPredecessor()定位key所在的层次以及找到key的前继节点(b),然后找到b的后继节点n。
第2步:根据“key的前继节点(b)”和“key的前继节点的后继节点(n)”来定位“key对应的节点”。
具体是通过比较“n的键值”和“key”的大小。如果相等,则n就是所要查找的键。

 

ConcurrentSkipListMap示例

复制代码
import java.util.*;import java.util.concurrent.*;/* *   ConcurrentSkipListMap是“线程安全”的哈希表,而TreeMap是非线程安全的。 * *   下面是“多个线程同时操作并且遍历map”的示例 *   (01) 当map是ConcurrentSkipListMap对象时,程序能正常运行。 *   (02) 当map是TreeMap对象时,程序会产生ConcurrentModificationException异常。 * * @author skywang */public class ConcurrentSkipListMapDemo1 {    // TODO: map是TreeMap对象时,程序会出错。    //private static Map<String, String> map = new TreeMap<String, String>();    private static Map<String, String> map = new ConcurrentSkipListMap<String, String>();    public static void main(String[] args) {            // 同时启动两个线程对map进行操作!        new MyThread("a").start();        new MyThread("b").start();    }    private static void printAll() {        String key, value;        Iterator iter = map.entrySet().iterator();        while(iter.hasNext()) {            Map.Entry entry = (Map.Entry)iter.next();            key = (String)entry.getKey();            value = (String)entry.getValue();            System.out.print("("+key+", "+value+"), ");        }        System.out.println();    }    private static class MyThread extends Thread {        MyThread(String name) {            super(name);        }        @Override        public void run() {                int i = 0;            while (i++ < 6) {                // “线程名” + "序号"                String val = Thread.currentThread().getName()+i;                map.put(val, "0");                // 通过“Iterator”遍历map。                printAll();            }        }    }}
复制代码

(某一次)运行结果

 

复制代码
(a1, 0), (a1, 0), (b1, 0), (b1, 0),(a1, 0), (b1, 0), (b2, 0), (a1, 0), (a1, 0), (a2, 0), (a2, 0), (b1, 0), (b1, 0), (b2, 0), (b2, 0), (b3, 0), (b3, 0), (a1, 0), (a2, 0), (a3, 0), (a1, 0), (b1, 0), (a2, 0), (b2, 0), (a3, 0), (b3, 0), (b1, 0), (b4, 0), (b2, 0), (a1, 0), (b3, 0), (a2, 0), (b4, 0), (a3, 0), (a1, 0), (a4, 0), (a2, 0), (b1, 0), (a3, 0), (b2, 0), (a4, 0), (b3, 0), (b1, 0), (b4, 0), (b2, 0), (b5, 0), (b3, 0), (a1, 0), (b4, 0), (a2, 0), (b5, 0), (a3, 0), (a1, 0), (a4, 0), (a2, 0), (a5, 0), (a3, 0), (b1, 0), (a4, 0), (b2, 0), (a5, 0), (b3, 0), (b1, 0), (b4, 0), (b2, 0), (b5, 0), (b3, 0), (b6, 0), (b4, 0), (a1, 0), (b5, 0), (a2, 0), (b6, 0), (a3, 0), (a4, 0), (a5, 0), (a6, 0), (b1, 0), (b2, 0), (b3, 0), (b4, 0), (b5, 0), (b6, 0), 
复制代码

结果说明
示例程序中,启动两个线程(线程a和线程b)分别对ConcurrentSkipListMap进行操作。以线程a而言,它会先获取“线程名”+“序号”,然后将该字符串作为key,将“0”作为value,插入到ConcurrentSkipListMap中;接着,遍历并输出ConcurrentSkipListMap中的全部元素。 线程b的操作和线程a一样,只不过线程b的名字和线程a的名字不同。
当map是ConcurrentSkipListMap对象时,程序能正常运行。如果将map改为TreeMap时,程序会产生ConcurrentModificationException异常。

 


更多内容

1. Java多线程系列--“JUC集合”01之 框架

2. Java多线程系列目录(共xx篇)

 

0 0