Concurrency
Ordering between threads, without polling. Your largest gap, and the email details it.
Trigger in the prompt: 'Threads', 'in order', 'alternate', 'at most N at a time', 'deadlock'.
easyPrint in Order
Three threads call three methods; force them to run first, second, third.
TriggerAny wording of the form 'guarantee this order between threads'.
Approach- State the problem: the OS gives no ordering guarantee, so I have to impose one.
- Two signals — an Event or a Semaphore starting at zero for each dependency.
- first() runs, then releases signal A. second() waits on A, runs, releases B.
- third() waits on B. No busy-waiting, no sleeps.
O(1) per call. Each thread blocks at most once.
PitfallUsing a shared flag plus a spin loop. It burns CPU and is not a synchronisation primitive.
Say it out loud“There's no ordering guarantee between threads by default, so I need explicit signalling. I'll use two semaphores initialised to zero — each method waits on its predecessor's semaphore and releases its own. That's a happens-before edge, and it costs no polling.”
Solve on LeetCodemediumBuilding H2O
Threads representing hydrogen and oxygen must group into complete molecules before releasing.
Trigger'Assemble groups of N before anyone proceeds' — a barrier with counts.
Approach- Two counted semaphores: hydrogen with 2 permits, oxygen with 1.
- A Barrier of 3 makes the molecule complete before any of the three returns.
- After the barrier, release the permits back so the next molecule can form.
O(1) per thread. The barrier costs one context switch per participant.
PitfallReleasing the permits before the barrier. Then a fourth hydrogen slips into the next molecule and you emit an invalid grouping.
Say it out loud“This is a barrier with capacity constraints. The semaphores cap how many of each atom can be in flight; the barrier is what makes the grouping atomic. The subtle part is releasing the permits after the barrier, not before.”
Solve on LeetCodemediumFizz Buzz Multithreaded
Four threads split the FizzBuzz output, each responsible for one case.
TriggerSeveral threads, one shared counter, and a condition that selects who acts.
Approach- One Condition guarding a shared counter i.
- Each thread loops: acquire, `while` the counter is not mine and i <= n, wait.
- Act, increment, notify_all, release.
O(n) time. Each increment wakes every waiter, so it is O(n) wakeups in the worst case.
Pitfall`notify()` instead of `notify_all()`. With four threads waiting on different conditions, you can wake the wrong one and the system stalls.
Say it out loud“Because the four threads wait on different predicates on the same condition variable, I have to use notify_all — notify could wake a thread whose predicate is still false and nobody would make progress.”
Solve on LeetCodemediumPrint FooBar Alternately
Two threads must alternate strictly, printing foo then bar, n times.
Trigger'Alternate', 'take turns', 'strictly interleave'.
Approach- Two semaphores: one starts at 1 (foo may go), the other at 0.
- foo acquires its own, prints, releases bar's. bar does the mirror image.
- The invariant: exactly one semaphore has a permit at any moment.
O(n) total, O(1) space. Each thread blocks n times.
PitfallOne lock plus a boolean and an `if`: the thread can wake, find it is not its turn, and either spin or deadlock. Use a `while`, or use two semaphores.
Say it out loud“Alternation is a ping-pong of permits. I'll start one semaphore at one and the other at zero, so the invariant is that exactly one permit exists — that makes strict alternation structural rather than something I have to check.”
Solve on LeetCodemediumPrint Zero Even Odd
Three threads cooperate to print 0102030405…, one owning zero, one even, one odd.
TriggerThree or more threads with a repeating cyclic pattern.
Approach- Three semaphores; zero starts at 1, the other two at 0.
- zero() runs every iteration and hands the permit to odd or even based on the parity of i.
- The parity decision lives in one place — the zero thread — so there is no shared mutable state.
O(n) time, O(1) space.
PitfallPutting the parity logic in every thread. Then two threads read a shared counter and you have a race on top of a synchronisation problem.
Say it out loud“I'll centralise the decision: the zero thread is the scheduler and hands the turn to odd or even. That way the parity check happens in exactly one place, and the other two threads never read shared state.”
Solve on LeetCodemediumThe Dining Philosophers
Five philosophers share five forks; each needs both neighbours' forks to eat.
TriggerMultiple resources needed together, held by different actors — the deadlock question.
Approach- Name the deadlock first: if everyone picks up their left fork, all four Coffman conditions hold.
- Break circular wait: impose a global ordering — every philosopher takes the lower-numbered fork first.
- Alternatives worth naming: a semaphore limiting four diners at once, or asymmetry (odd philosophers reverse their order).
O(1) per meal. The lock ordering costs nothing at runtime.
PitfallAdding a timeout and retry as the 'fix'. That converts deadlock into livelock unless you add randomised backoff.
Say it out loud“The naive solution deadlocks: everyone grabs their left fork and the wait graph has a cycle. I'll break circular wait with a global lock ordering — always acquire the lower-numbered fork first. That's cheap, it's local, and it doesn't change the program's semantics.”
Solve on LeetCode