Advanced 28 min readModule: Module 14: Lock-Free Concurrency Algorithms: Stacks & Queues
Lock-Free Algorithms: Treiber Stack & Michael-Scott Queue
Design wait-free and lock-free concurrent algorithms: Compare-And-Swap (CAS) consensus, lock-free Treiber Stack, the Michael-Scott non-blocking FIFO Queue, and solving the ABA problem with tagged pointers.
What You Will Learn in This Lesson
- The hierarchy of non-blocking concurrency: Obstruction-Free vs Lock-Free vs Wait-Free
- Atomic hardware primitives: Compare-And-Swap (CAS) and Fetch-And-Add (FAA)
- The Michael-Scott Lock-Free Queue algorithm (foundation of Java's ConcurrentLinkedQueue)
- The ABA Problem and solving it with versioned/tagged atomic pointers
Introduction & Core Concept
Concurrent data structures that rely on mutual exclusion locks (mutexes) suffer from priority inversion, deadlock risks, and high context-switching overhead. Lock-free algorithms guarantee system-wide progress without locks: even if some threads are suspended or delayed by the OS, at least one thread is guaranteed to complete its operation in a bounded number of steps.
WHY DOES THIS MATTER IN THE REAL WORLD?
High-throughput asynchronous runtimes (Tokio, .NET ThreadPool, Go runtime) and financial trading systems rely on lock-free stacks and queues to schedule millions of tasks per second without lock contention.
Syntax & Structure
javascript
while (!CAS(&head, oldHead, newHead)) { oldHead = head;}Simulating a Lock-Free Michael-Scott Non-Blocking FIFO Queue with Atomic CAS
javascriptjavascript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677// Conceptual Lock-Free Michael-Scott Queue Simulationclass Node {constructor(value) {this.value = value;this.next = null;}}class LockFreeQueue {constructor() {// Dummy sentinel nodeconst sentinel = new Node(null);this.head = sentinel;this.tail = sentinel;}// Atomic CAS Simulation Helper_cas(obj, field, expected, update) {if (obj[field] === expected) {obj[field] = update;return true;}return false;}enqueue(val) {const newNode = new Node(val);while (true) {const curTail = this.tail;const tailNext = curTail.next;if (curTail === this.tail) {if (tailNext === null) {// Try to link newNode at the end of the listif (this._cas(curTail, 'next', null, newNode)) {// Advance tail to new node (Helpful step)this._cas(this, 'tail', curTail, newNode);return;}} else {// Tail was lagging behind; help advance itthis._cas(this, 'tail', curTail, tailNext);}}}}dequeue() {while (true) {const curHead = this.head;const curTail = this.tail;const headNext = curHead.next;if (curHead === this.head) {if (curHead === curTail) {if (headNext === null) return null; // Queue Empty// Advance lagging tailthis._cas(this, 'tail', curTail, headNext);} else {const value = headNext.value;if (this._cas(this, 'head', curHead, headNext)) {return value; // Dequeued successfully!}}}}}}const queue = new LockFreeQueue();queue.enqueue("TASK_101");queue.enqueue("TASK_102");console.log("=== Lock-Free Michael-Scott Queue ===");console.log("Dequeued:", queue.dequeue());console.log("Dequeued:", queue.dequeue());console.log("Dequeued (Empty):", queue.dequeue());console.log("✅ Queue executed without locking or thread suspension!");
Line-by-Line Technical Breakdown
1The ABA Problem: Thread 1 reads pointer A. Thread 2 pops A, frees it, pushes B, and pushes a newly allocated node that happens to share the same physical address A. Thread 1's CAS succeeds even though the list changed. Tagged pointers (storing a 16-bit incrementing counter alongside the pointer) prevent the ABA problem.
Try It Yourself (Interactive Editor)
Modify the code in real-time and click Run to test live browser output and console logs.
Intelligent Code Runner & Live Sandbox[JAVASCRIPT]
JAVASCRIPT SOURCE EDITOR
Interactive Live CodeCommon Mistakes & How to Avoid Them
#1: Assuming lock-free algorithms are always faster than mutex locks for low-contention single-threaded workloads.
Under zero contention, simple sequential data structures are faster due to lack of memory barrier instructions. Lock-free shines under concurrent thread contention.
Incorrect / Antipattern
// Using complex CAS retry loops on thread-confined collectionsCorrect / Professional Solution
// Use lock-free structures specifically when multiple threads contend concurrentlyIndustry Best Practices & Professional Standards
- Use tagged/versioned pointers (`AtomicStampedReference` in Java) to eliminate the ABA problem.
- Use helping mechanisms so lagging threads do not block forward progress of active threads.
- Rely on battle-tested standard libraries (`ConcurrentLinkedQueue`, `crossbeam::queue`) in production.
Lesson Summary & Core Takeaways
- Lock-free algorithms guarantee system-wide forward progress using hardware atomic CAS instructions.
- The Michael-Scott Queue provides high-concurrency FIFO buffering without mutex locks.
- Tagged pointers and epoch memory reclamation solve the concurrent ABA problem.