LLD
Concurrency Primitives — Detailed
flowchart TB
subgraph Mutual_Exclusion[Mutual exclusion]
MTX([Mutex / Lock<br/>one holder])
SPIN([Spinlock<br/>busy-wait])
REC([Reentrant lock])
end
subgraph Capacity[Capacity]
SEM([Semaphore<br/>N permits])
BIN([Binary semaphore])
end
subgraph ReadWrite[Read-heavy access]
RW([RW lock<br/>many readers, one writer])
STAMP([Stamped lock])
end
subgraph Coordination[Coordination]
CV([Condition variable])
LATCH([Countdown latch])
BAR([Cyclic barrier])
PHA([Phaser])
end
subgraph LockFree[Lock-free / atomic]
ATM([Atomic int / ref])
CAS([Compare-and-swap])
MV([Memory ordering<br/>acquire / release])
end
subgraph Isolation[Isolation]
TL([Thread-local])
IMM([Immutable shared])
end
classDef s fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
class MTX,SPIN,REC,SEM,BIN,RW,STAMP,CV,LATCH,BAR,PHA,ATM,CAS,MV,TL,IMM s;
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class RW client;
class MTX,SPIN,REC,SEM,BIN,STAMP,CV,LATCH,BAR,PHA,ATM,CAS,MV,TL service;
class IMM datastore;
Cheat sheet
Primitive
What it does
When to use
Mutex / Lock
one holder at a time
exclusive access to mutable state
Spinlock
busy-waits; no kernel call
very short critical sections, no scheduler help
Reentrant lock
same thread can re-acquire
recursive code paths under one lock
Semaphore (counting)
N concurrent permits
rate-limit / connection pool / bounded resource
Binary semaphore
0 or 1
signal between threads
Read-Write lock
many readers OR one writer
read-heavy maps / caches
Condition variable
wait until predicate
producer/consumer queue, blocking get
Countdown latch
block until N events
"wait for 5 workers to start"
Cyclic barrier
N threads must reach point
iterative algorithms
Atomic
lock-free ops on a single word
counters, refs
Compare-and-swap (CAS)
atomic "if val==X set to Y"
lock-free queues, optimistic concurrency
Thread-local
per-thread storage
request id, current user, ThreadLocalRandom
Immutability
no shared mutation
safest concurrency
Producer/consumer with a condition variable
sequenceDiagram
participant P as Producer
participant Q as Queue
participant C as Consumer
P->>Q: lock, put item, notify, unlock
Note over C: wait on cv if empty
Q-->>C: notify
C->>Q: lock, take item, unlock
class BlockingQueue < T > {
private final Deque < T > q = new ArrayDeque <> ();
private final int cap ;
private final ReentrantLock lock = new ReentrantLock ();
private final Condition notFull = lock . newCondition ();
private final Condition notEmpty = lock . newCondition ();
void put ( T x ) throws InterruptedException {
lock . lock ();
try {
while ( q . size () == cap ) notFull . await ();
q . addLast ( x );
notEmpty . signal ();
} finally { lock . unlock (); }
}
T take () throws InterruptedException {
lock . lock ();
try {
while ( q . isEmpty ()) notEmpty . await ();
T x = q . removeFirst ();
notFull . signal ();
return x ;
} finally { lock . unlock (); }
}
}
CAS-based counter
final AtomicLong counter = new AtomicLong ( 0 );
counter . incrementAndGet (); // lock-free
counter . compareAndSet ( 5 , 6 ); // boolean
Pitfalls
Race condition : result depends on thread interleaving (read-modify-write without locking).
Visibility : a value written by one thread isn't seen by another without proper memory ordering (volatile, atomics, locks).
Re-entrancy traps : calling back into your own lock from a callback — use reentrant locks or refactor.
Spurious wakeups : condition.await() can return without a notification — always re-check in a while loop.
Lost wakeups : notify before await is called — keep state guarded by the same lock as the notify/await.
Where this shows up in this site
Distributed lock service — same semantics at network scale.
Rate limiter — counting semaphore in spirit.
Stock exchange matching engine — single-threaded per symbol to avoid these primitives entirely.
Logger framework / Background jobs / Distributed counter — extensive use of atomics + condition variables.
Glossary & fundamentals
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
Tag
Concept
What it is
Page
HLD
CAP / PACELC
C vs A under partition; L vs C otherwise
cap-pacelc
LLD
Concurrency primitives
mutex, semaphore, RW lock, atomic, CAS
concurrency-primitives
LLD
Threading & deadlocks
thread states, Coffman, lock ordering
threading-and-deadlocks
LLD
Immutability
immutable types, persistent collections
immutability
LLD
Error handling
exceptions vs Result, error boundaries
error-handling