Skip to content

Commit ce1bcb9

Browse files
committed
ConcurrentSkipListMap — Internal working.
Signed-off-by: https://github.com/Someshdiwan <[email protected]>
1 parent 08738c9 commit ce1bcb9

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
ConcurrentSkipListMap — Internal working:
2+
3+
What it is (high level)
4+
5+
ConcurrentSkipListMap is a concurrent, sorted map implementation in java.util.concurrent.
6+
It implements ConcurrentNavigableMap and stores entries in sorted order (natural ordering or via a Comparator).
7+
8+
Internally it uses a skip list data structure, which provides expected O(log n) time for get, put, remove, and
9+
navigational operations.
10+
11+
It is designed for highly concurrent access without global locks — reads are essentially lock-free and many writes
12+
proceed concurrently.
13+
14+
15+
16+
When to use it (use-cases)
17+
• You need a thread-safe, sorted map with non-blocking or low-lock concurrency behavior.
18+
• Useful for concurrent ordered-index scenarios: scheduling, time-indexed events, priority tables, caches that require sorted keys.
19+
• Prefer ConcurrentSkipListMap over TreeMap when concurrency is required (TreeMap is not thread-safe).
20+
21+
22+
23+
Key properties
24+
• Thread-safe and highly concurrent.
25+
• Sorted order guaranteed (Comparator or natural order).
26+
• Non-blocking reads (lock-free or minimal internal synchronization).
27+
• Expected O(log n) for most operations.
28+
• Iterators are weakly consistent: they reflect some, but not necessarily all,
29+
updates since the iterator was created and they do not throw ConcurrentModificationException.
30+
• No null keys or null values allowed.
31+
32+
33+
34+
How a skip list works (brief theory)
35+
36+
A skip list stores the same elements in multiple levels of linked lists.
37+
Level 0 is the base sorted linked list (all elements).
38+
Higher levels contain increasingly sparse subsets of elements;
39+
each higher level lets you “skip” across many nodes.
40+
41+
To search, you start at the top level and move right as long as the next key is less than the target;
42+
when you can’t move right, drop down one level and repeat. This gives average O(log n) search time.
43+
44+
In ConcurrentSkipListMap the nodes and links are implemented with CAS-friendly fields so multiple threads can
45+
update different parts concurrently.
46+
47+
48+
49+
ASCII diagram — skip list structure (simple)
50+
51+
Level 3: HEAD -----------------------> [ 50 ] -----------------> [ 90 ] -> null
52+
skip many skip many
53+
54+
Level 2: HEAD ---------------> [ 20 ] ----------> [ 50 ] ---> [ 90 ] -> null
55+
56+
Level 1: HEAD ---------> [ 10 ] ---> [ 20 ] ---> [ 30 ] ---> [ 50 ] ---> [ 70 ] ---> null
57+
58+
Level 0: HEAD -> [ 5 ] -> [10] -> [15] -> [20] -> [25] -> [30] -> [40] -> [50] -> [70] -> [90] -> null
59+
60+
Search for 40:
61+
• Start at Level 3 HEAD: move right while next < 40, then drop levels until Level 0, then traverse to exact node.
62+
63+
Insert 40:
64+
• Determine levels for new node (randomized in classic skip list; in concurrent implementation, there are probabilistic choices).
65+
• CAS link updates are used to splice new node into lists at each level.
66+
67+
68+
69+
Concurrent aspects (how concurrency is handled)
70+
• ConcurrentSkipListMap uses non-blocking algorithms and CAS on node fields to update links. It avoids coarse-grained locks.
71+
• Readers are not blocked by writers: traversals can proceed while updates happen, and iterators are weakly consistent.
72+
• Structural updates use careful CAS loops and “helping” strategies (threads may help finish partially-completed updates) to maintain correctness.
73+
• The data structure tolerates concurrent puts/removes with minimal contention because operations affect a small local portion of the list (near the key’s neighbors).
74+
75+
76+
77+
Complexity summary
78+
• get(K key): expected O(log n) (search down levels).
79+
• put(K key, V value): expected O(log n) (find position + link insert).
80+
• remove(K key): expected O(log n) (find + CAS unlink).
81+
• firstKey()/lastKey(): O(log n) to locate ends.
82+
• subMap, headMap, tailMap: return views backed by the map; operations are O(log n) per access.
83+
84+
85+
86+
Important characteristics & semantics
87+
• Sorted order maintained at all times.
88+
• Weakly-consistent iterators: they reflect some snapshot but allow concurrent modification.
89+
• No null keys/values: attempts will throw NullPointerException.
90+
• Thread-safety guaranteed by internal CAS and fine-grained coordination; external synchronization not required.
91+
92+
93+
94+
Main API highlights (useful methods)
95+
• V put(K key, V value)
96+
• V get(Object key)
97+
• V remove(Object key)
98+
• K firstKey(), K lastKey()
99+
• NavigableSet<K> keySet(), NavigableMap<K,V> subMap(K fromKey, K toKey, boolean fromInclusive, boolean toInclusive)
100+
101+
102+
103+
Notes on the example
104+
• Writers run concurrently and insert keys.
105+
• Iteration during writes is allowed — the iterator is weakly consistent (it won’t throw ConcurrentModificationException).
106+
• Final map content is in sorted order.
107+
108+
109+
110+
Internals — more detailed (implementation notes)
111+
• Node structure: nodes contain key, value, and arrays or links to next nodes at different levels.
112+
• CAS on node next pointers is used to insert/delete safely; some algorithms use marked flags to indicate logically removed nodes before physical unlinking.
113+
• The map uses probabilistic level assignment (or similar heuristics) to decide how many levels a new node spans — this produces balanced performance on average.
114+
• Unlike balanced trees (like red-black trees), skip lists trade a small constant factor in performance for simpler, highly concurrent update algorithms.
115+
116+
117+
118+
Comparisons
119+
• ConcurrentSkipListMap vs TreeMap: TreeMap is balanced tree (Red-Black), not thread-safe; ConcurrentSkipListMap is concurrent and lock-free-friendly with similar O(log n) guarantees but better for concurrent use.
120+
• ConcurrentSkipListMap vs ConcurrentHashMap: ConcurrentHashMap is for unordered maps with better throughput for pure key-based lookup; ConcurrentSkipListMap provides ordering and navigable operations at cost of slightly higher per-op overhead.
121+
• ConcurrentSkipListMap vs ConcurrentSkipListSet: the set is a thin wrapper over ConcurrentSkipListMap keys.
122+
123+
124+
125+
Pros and cons
126+
127+
Pros:
128+
• Thread-safe sorted map with high concurrency.
129+
• Navigable operations available (lowerKey, higherKey, ceiling, floor, etc.).
130+
• Iterators are safe for concurrent use (weakly consistent).
131+
132+
Cons:
133+
• Higher memory overhead (multiple levels, extra pointers).
134+
• Slightly slower per-op constants than highly-optimized unordered concurrent maps like ConcurrentHashMap.
135+
• Randomized structure — guarantees are probabilistic (but reliable in practice).
136+
137+
138+
139+
ASCII flow for insertion (concurrent-friendly view)
140+
141+
1) Find insertion point starting from the top level
142+
- traverse right while next.key < key
143+
- when can't move right, move down
144+
145+
2) Once position at each level decided, attempt CAS on next at that level:
146+
oldNext = prev.next[level]
147+
if CAS(prev.next[level], oldNext, newNode with next=oldNext) succeeds -> level linked
148+
else -> retry (helping or re-search)
149+
150+
3) After linking all required levels, insertion is visible to readers.
151+
152+
4) If concurrent removals occur, removal marks nodes and then unlinks them with CAS.
153+
154+
155+
156+
157+
Practical tips
158+
• Because iterators are weakly consistent, they’re excellent for diagnostics and non-critical scans — they give a snapshot-ish view without blocking writers.
159+
• Use when both ordering and concurrency are important (e.g., time-ordered event processing).
160+
• Avoid if you need strict snapshot semantics — use external synchronization or copy-on-write patterns.
161+
162+

0 commit comments

Comments
 (0)