concurrent data structure
**Concurrent Data Structures** is the **design and implementation of data structures that support simultaneous access by multiple threads without data corruption, using fine-grained locking, lock-free algorithms, or transactional memory to maximize parallelism while maintaining correctness** — the foundation of scalable multi-threaded software. The choice of concurrent data structure — from a simple mutex-protected container to a sophisticated lock-free skip list — determines whether a parallel application scales to 64 cores or serializes at a single bottleneck.
**Concurrency Correctness Requirements**
- **Safety (linearizability)**: Every operation appears to take effect atomically at some point between its invocation and response — as if executed sequentially.
- **Liveness (progress)**: Operations eventually complete, not blocked indefinitely.
- **Progress conditions** (strongest to weakest):
- **Wait-free**: Every thread completes in a bounded number of steps regardless of others.
- **Lock-free**: At least one thread makes progress in a bounded number of steps.
- **Obstruction-free**: A thread makes progress if it runs in isolation.
- **Blocking**: Other threads can prevent progress (mutex-based).
**Concurrent Queue Implementations**
**1. Mutex-Protected Queue (Simple)**
- Single lock protects entire queue → safe but serializes all enqueue/dequeue.
- Throughput: ~1 operation per mutex acquisition → linear throughput regardless of cores.
**2. Two-Lock Queue (Michael-Scott)**
- Separate locks for head (dequeue) and tail (enqueue).
- Producers and consumers operate concurrently as long as queue is non-empty.
- 2× throughput improvement when producers and consumers run simultaneously.
**3. Lock-Free Queue (Michael-Scott CAS-based)**
- Uses Compare-And-Swap (CAS) atomic operation instead of lock.
- Enqueue: CAS to swing tail pointer to new node → linearization point.
- Dequeue: CAS to swing head pointer → remove node.
- Lock-free: Even if one thread stalls, others can complete their operations.
- Challenge: ABA problem → need tagged pointers or hazard pointers.
**4. Disruptor (Ring Buffer)**
- Pre-allocated ring buffer, cache-line-padded sequence numbers.
- No allocation per operation → cache-friendly → very high throughput.
- Used by: LMAX Exchange (financial trading), logging frameworks.
- Throughput: 50+ million operations/second vs. 5 million for ConcurrentLinkedQueue.
**Concurrent Hash Map**
**Java ConcurrentHashMap (JDK 8+)**
- Stripe-level locking: Lock individual linked-list heads (buckets).
- Concurrent reads: Fully parallel (volatile reads, no lock for non-structural reads).
- Concurrent writes to different buckets: Fully parallel (different locks).
- Treeify: Bucket chains longer than 8 → convert to red-black tree → O(log n) per bucket.
**Lock-Free Hash Map**
- Split-ordered lists (Shalev-Shavit): Lock-free ordered linked list + on-demand bucket allocation.
- Each bucket is a sentinel in the ordered list → CAS for insert/delete → fully lock-free.
- Hopscotch hashing: Better cache behavior than chaining → faster for dense maps.
**Fine-Grained Locking Patterns**
**1. Lock Coupling (Hand-over-Hand)**
- For linked list traversal: Lock node i → lock node i+1 → release node i → advance.
- Allows concurrent operations at different parts of the list.
- Used for: Concurrent sorted lists, B-tree traversal.
**2. Read-Write Lock**
- Multiple concurrent readers allowed; exclusive writer.
- `pthread_rwlock_t`, `std::shared_mutex` (C++17).
- Read-heavy workloads: Near-linear read scaling; writes serialize.
**3. Sequence Lock (seqlock)**
- Writer increments sequence number (odd during write, even otherwise).
- Reader reads sequence → reads data → reads sequence again → if same and even → data consistent.
- Lock-free readers: Readers never block (can retry if writer intervenes).
- Used in Linux kernel for jiffies, time-of-day clock.
**ABA Problem and Solutions**
- CAS sees value A → something changes A→B→A → CAS succeeds incorrectly (value looks unchanged).
- Solutions:
- **Tagged pointers**: High bits of pointer encode version counter → prevents ABA.
- **Hazard pointers**: Thread registers pointer before use → garbage collector cannot free → safe memory reclamation.
- **RCU (Read-Copy-Update)**: Readers never blocked → writers create new version → reader sees consistent snapshot.
Concurrent data structures are **the engineering foundation that separates programs that scale from programs that serialize** — choosing the right concurrent container for each use case, understanding the tradeoffs between locking and lock-free approaches, and correctly implementing memory reclamation are the skills that determine whether a parallel system delivers 64× speedup on 64 cores or runs no faster than on 2 cores at the bottleneck data structure.