Skip to content

Commit 08738c9

Browse files
committed
ConcurrentSkipListMap and ConcurrentSkipList —
Detailed Explanation Signed-off-by: https://github.com/Someshdiwan <[email protected]>
1 parent 2d188e6 commit 08738c9

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
ConcurrentSkipListMap and ConcurrentSkipList — Detailed Explanation
2+
3+
Quick summary
4+
• ConcurrentSkipListMap
5+
A concurrent, sorted map implementation in java.util.concurrent. It implements ConcurrentNavigableMap<K,V>.
6+
Internally it uses a skip-list to store key/value entries in sorted order.
7+
It is designed for high concurrency: reads are essentially non-blocking and many writes can proceed without global locks.
8+
Iterators are weakly consistent (they tolerate concurrent updates and do not throw ConcurrentModificationException).
9+
10+
• ConcurrentSkipList (usually referring to ConcurrentSkipListSet)
11+
A concurrent, sorted set built on top of a ConcurrentSkipListMap (the set is essentially the map’s keys).
12+
In Java the class is ConcurrentSkipListSet<E>; it implements ConcurrentNavigableSet<E>.
13+
It provides the same concurrency and ordering properties as the map, but for unique keys only.
14+
15+
16+
17+
Core idea: what is a skip list?
18+
19+
A skip list is a layered linked-list structure that provides probabilistic balancing:
20+
• Level 0: a standard sorted singly linked list containing all nodes.
21+
• Level 1..L: higher levels contain a subset of nodes, allowing you to “skip” many items quickly.
22+
• Searching: start at the top level, move right until the next key would overshoot,
23+
then drop down one level and repeat until level 0.
24+
• Average-case performance: operations (search/insert/delete) are expected O(log n).
25+
The balancing comes from random or probabilistic promotion of nodes to higher levels.
26+
27+
ASCII sketch (small):
28+
29+
Level 3: HEAD -------------------------> [50] -----------------> null
30+
Level 2: HEAD --------------> [20] -> [50] -> [90] -> null
31+
Level 1: HEAD -----> [10] -> [20] -> [30] -> [50] -> null
32+
Level 0: HEAD -> [5] -> [10] -> [15] -> [20] -> [25] -> [30] -> [40] -> [50] -> null
33+
34+
35+
36+
37+
Why skip lists for concurrent sorted maps/sets?
38+
• Skip lists are simpler to make concurrent than balanced trees. Their updates are local (affect a few pointers)
39+
and can be implemented using CAS (compare-and-swap) on pointer fields.
40+
• They allow a high degree of concurrency because different threads often touch different parts of the structure.
41+
• Readers can traverse with minimal synchronization and remain safe (weakly consistent) while writers make changes.
42+
43+
44+
45+
Internal structure (high level)
46+
• Node: contains key, value (map only), and an array or sequence of forward/next pointers—one per level that node appears in.
47+
• Head: a sentinel node with highest level pointers.
48+
• Level management: when inserting a node, you choose how many levels it participates in
49+
(traditionally by coin-flip probability or an equivalent RNG). Higher-level pointers skip farther.
50+
• Atomic updates: link/unlink operations use atomic CAS on next pointers.
51+
A removal may be done in two phases: logical removal (marking) then physical unlinking (CAS to remove).
52+
• Helping: concurrent algorithms often let other threads “help” finish a partially-completed update to avoid
53+
inconsistent state and reduce retries.
54+
• Weakly-consistent iterators: iteration walks next pointers and tolerates concurrently inserted/removed nodes.
55+
The iterator may or may not see every concurrent update.
56+
57+
58+
59+
How operations work (conceptually)
60+
61+
Search (get / contains):
62+
• Start at top level, move right while next key < target. When you cannot move right, drop down a level.
63+
Continue until level 0 and then inspect candidate node.
64+
65+
Insert (put / add):
66+
1. Locate predecessor nodes at each level where new node will link in.
67+
2. Allocate new node and set its forward pointers to successors.
68+
3. Using CAS, splice the new node at each level, typically from lowest to highest or vice versa, with retries on CAS failure.
69+
4. If collisions or competing updates occur, retry the local CAS or help complete concurrent updates.
70+
71+
Remove (delete / remove):
72+
1. Find node and mark it logically removed (often by setting a flag or special value in the next pointer).
73+
2. Physically unlink node by CAS on predecessor.next to bypass the removed node.
74+
3. Other threads may help unlink.
75+
76+
Because operations touch a small neighbourhood of pointers, different keys can be manipulated concurrently with low contention.
77+
78+
79+
80+
Concurrency model & guarantees
81+
• Thread-safety: built-in; no external synchronization needed.
82+
• Locking: mostly lock-free or fine-grained CAS; no single global lock.
83+
• Iterators: weakly consistent — they traverse elements reflecting some state between creation and end, but they do
84+
not throw ConcurrentModificationException.
85+
• Progress: operations make progress without blocking; writers may retry operations under contention but do not block
86+
other threads generally.
87+
88+
89+
90+
Complexity
91+
• Average/expected time: O(log n) for get, put, remove, contains.
92+
• Memory: extra pointers per node (one per level) increase memory overhead compared to a plain linked list;
93+
expected levels per node is constant (geometric distribution).
94+
• Worst-case (degenerate) is O(n) but rare because of probabilistic balancing.
95+
96+
97+
98+
Key differences: ConcurrentSkipListMap vs ConcurrentSkipListSet
99+
• ConcurrentSkipListMap<K,V>
100+
• Stores key→value entries; supports full Map and NavigableMap APIs.
101+
• Supports operations like put, get, remove, firstKey, subMap, ceilingKey, and other navigable methods.
102+
• Good when you need sorted associations (key → value).
103+
• ConcurrentSkipListSet
104+
• Backed by a ConcurrentSkipListMap<E, Boolean> or similar internal structure (the set is basically the map’s keys).
105+
• Provides set-style API (add, remove, contains, first, etc.).
106+
• Use when you only need a sorted collection of unique elements.
107+
108+
109+
110+
Typical use cases
111+
• Concurrent ordered indexes (time-based events, scheduling).
112+
• Concurrent caches or registries where keys must be sorted or you need navigable operations (range queries).
113+
• Multi-threaded environments where you need non-blocking reads with frequent concurrent updates.
114+
• Implementing priority-like behavior when keys represent priorities or time-stamps.
115+
116+
117+
118+
Comparisons with other concurrent maps
119+
• ConcurrentSkipListMap vs TreeMap
120+
• TreeMap is not thread-safe and is based on red-black trees. ConcurrentSkipListMap is thread-safe and better for
121+
concurrent access with similar O(log n) guarantees.
122+
• ConcurrentSkipListMap vs ConcurrentHashMap
123+
• ConcurrentHashMap is unordered and optimized for raw throughput (hash-based).
124+
If you do not need ordering, prefer ConcurrentHashMap for higher throughput.
125+
If you need ordering or navigable methods, use ConcurrentSkipListMap.
126+
• ConcurrentSkipListMap vs ConcurrentSkipListSet
127+
• Set is just the keys view; choose whichever matches your need (map for key→value, set for unique keys only).
128+
129+
130+
131+
Practical considerations & tips
132+
• Avoid null keys and values: these implementations do not permit null keys or null values.
133+
• Iterators are good for monitoring and non-critical scans; they are not snapshots and may not reflect all updates.
134+
• If you need strict, consistent snapshots for range queries, consider copying data to a separate structure or using external synchronization.
135+
• Memory: be mindful of the per-node pointers — skip lists typically consume more memory than hash maps or single linked lists.
136+
• For ordered concurrent maps with navigational operations, ConcurrentSkipListMap is one of the best standard options.
137+
138+
139+
140+
ASCII example of concurrent insertion (conceptual)
141+
142+
Thread A inserts key 40:
143+
- Locates prev nodes at each needed level.
144+
- Attempts CAS on prev.level0.next from old -> new(40)->oldNext
145+
- If CAS succeeds at level0, proceeds to higher levels.
146+
- If level0 CAS loses (another thread inserted nearby), Thread A retries search and CAS.
147+
148+
Thread B concurrently inserts key 35:
149+
- It operates in a nearby region but uses CAS on pointers for its own prev nodes.
150+
- Both can succeed as long as they update different next pointers or CAS ordering allows.
151+
152+
153+
154+
Final summary
155+
• ConcurrentSkipListMap and ConcurrentSkipListSet are high-quality, concurrent, sorted collections built on skip lists.
156+
• They provide expected O(log n) performance, excellent concurrency (non-blocking reads, fine-grained updates), and navigable APIs.
157+
• Use them when you need sorted, thread-safe access with many concurrent readers/writers;
158+
use ConcurrentHashMap instead when ordering is not required and raw throughput is the highest priority.
159+
160+

0 commit comments

Comments
 (0)