See which thread runs what.

Concurrency in Java, Python, JavaScript, C#, Kotlin, Go and Rust, animated on real thread lanes: threads, locks, the GIL, channels and async tasks.

Java 19 scenarios

Basics

  1. 01Starting a threadstart() runs download() on a new thread, and main carries on at once. The JVM waits for worker before it exits.What-if: run() by mistake, daemon thread
  2. 02Stopping a threadinterrupt() asks a thread to stop. A sleeping thread answers at once, because sleep() throws InterruptedException.What-if: busy loop checks the flag, exception swallowed
  3. 03wait() vs sleep()napper sleeps inside synchronized. It stops running but keeps the lock, so other is BLOCKED the whole time.What-if: wait() releases it

Shared state

  1. 04Why count++ loses updatesTwo threads each run count++ once. The result can be 1, not 2.What-if: synchronized, AtomicInteger
  2. 05Why a thread never sees your flagmain sets stop = true, but worker keeps spinning forever. Without volatile, Java doesn't promise worker ever sees the write.What-if: volatile
  3. 06DeadlockT1 holds A and wants B. T2 holds B and wants A. Each waits for the other, forever.What-if: same lock order
  4. 07Thread-safe collectionsTwo threads add to one ArrayList. Both write into the same slot, and one element is lost.What-if: synchronizedList, ConcurrentHashMap
  5. 08Why ConcurrentModificationException happens with one threadA for-each loop removes bob from the list it is walking. The remove works; the loop's next step throws.What-if: removeIf, another thread adds, CopyOnWriteArrayList

Executors

  1. 09How a thread pool runs tasksA fixed pool runs as many tasks as it has threads. The rest wait in a queue for the next free thread.What-if: 4 threads
  2. 10Future.get() makes you waitsubmit() gives you a Future right away. get() trades it for the result, and blocks until the result exists.What-if: get() too early
  3. 11Why your Java program never exits (ExecutorService)main submits three tasks and returns without shutdown(). The tasks run, but the pool's threads wait for more work forever.What-if: shutdown(), shutdownNow()

CompletableFuture

  1. 12Which thread runs each step?supplyAsync → thenApply → thenAccept. Each step starts when the one before it finishes, and gets its result.What-if: thenApply vs thenApplyAsync
  2. 13Combining futuresDependent calls run one after another. Independent calls run side by side, then merge.What-if: allOf fan-out
  3. 14When a step throwsAn exception skips the next steps until something handles it. exceptionally runs only on failure; handle always runs.What-if: nobody handles it

Coordination

  1. 15Wait for N things with CountDownLatchmain waits until three loaders have each counted down once. Then the latch opens.What-if: a loader throws
  2. 16Limit concurrency with a SemaphoreFour threads want to call an API that allows two at a time. A Semaphore with 2 permits keeps the rest waiting.What-if: 1 permit
  3. 17Producer and consumer with a BlockingQueueA fast producer and a slow consumer share a queue with room for 2. The queue makes each one wait for the other.What-if: unbounded

Advanced

  1. 18When parallelStream starves the common poolA parallel stream of blocking calls fills the common pool. An unrelated quick task waits in line behind it.What-if: its own executor
  2. 19Virtual threadsThree virtual threads share two real (carrier) threads. When one blocks, it steps off its carrier so another can run.What-if: 2 platform threads

Python 4 scenarios

Choosing a model

  1. 01threading vs multiprocessing vs asyncioTwo jobs, each a network fetch and then CPU work. With threads, the fetches overlap but the CPU work takes turns on the GIL.What-if: multiprocessing, asyncio

Threads

  1. 02Why Python threads don't run in parallel (the GIL)Two threads crunch numbers. They take turns holding the GIL, so only one core is ever busy.What-if: I/O work, python3.15t

Processes

  1. 03multiprocessing.Pool: real parallelism with processesTwo worker processes crunch at the same time, each with its own interpreter and its own GIL.What-if: globals

Async

  1. 04time.sleep vs asyncio.sleep: blocking the event loopslow_call uses time.sleep inside a coroutine. It blocks the loop's only thread, so the ticker can't tick until it ends.What-if: await asyncio.sleep, asyncio.to_thread

JavaScript 3 scenarios

The event loop

  1. 01The JavaScript event loop: microtasks before timersOne thread runs the whole script first. Then every microtask (the promise), and only then the next task (the timeout).What-if: await, endless microtasks

One thread

  1. 02A CPU-heavy loop blocks the JavaScript event loopA ticker logs every 20 ms. A 200 ms busy loop holds the only JS thread, so no tick runs until it ends.What-if: Worker

Combining promises

  1. 03Promise.all vs awaiting one by oneEach await finishes before the next line runs, so fetchData("b") starts only after a is done.What-if: Promise.all

C# 3 scenarios

async and threads

  1. 01Which thread runs the code after await?FetchAsync starts on the caller's thread. After await Task.Delay, the rest of it, and of Main, runs on a thread-pool thread.What-if: Task.Run

Deadlocks

  1. 02The .Result deadlock on a UI threadA click handler blocks the UI thread in .Result. The rest of LoadAsync needs that same thread, so neither can go on.What-if: ConfigureAwait(false), await

Combining tasks

  1. 03Task.WhenAll vs awaiting one by oneEach await finishes before the next line runs, so FetchAsync("b") starts only after a is done.What-if: Task.WhenAll

Kotlin 3 scenarios

Coroutines and threads

  1. 01Kotlin coroutines take turns on one threadTwo coroutines share the main thread. Each delay() suspends one and frees the thread for the other.What-if: Thread.sleep()

Dispatchers

  1. 02Dispatchers.IO vs Dispatchers.Default for blocking callsFour blocking calls on Dispatchers.Default, on a 2-CPU machine. Only two run at a time; the others wait for a thread.What-if: Dispatchers.IO

Cancellation

  1. 03One failing coroutine cancels its siblingsInside coroutineScope, a throws. The scope cancels b, which is still in delay, and then rethrows a's exception.What-if: supervisorScope

Go 7 scenarios

Basics

  1. 01Why didn't my goroutines run?main starts three goroutines and returns. None of them get to print.What-if: wg.Wait()

Shared state

  1. 02Why count++ loses updates in goroutinesTwo goroutines each run count++ once. The result can be 1, not 2.What-if: sync.Mutex, atomic.Int64
  2. 07fatal error: concurrent map writes, explainedTwo goroutines write one plain map at the same time. The Go runtime notices and stops the whole program.What-if: sync.Mutex

Channels

  1. 03Does a channel send wait for the receiver?On an unbuffered channel, main's send waits until the receiver is ready to take the value.What-if: buffer of 1
  2. 04A worker pool on a channelTwo workers range over a jobs channel. close(jobs) tells them no more jobs are coming.What-if: forgot close
  3. 05A select timeout that leaks a goroutineThe timeout wins, but slowCall's goroutine is left stuck on its send, forever.What-if: buffer of 1, context

Cancellation

  1. 06Stopping goroutines with context cancellationThree workers share one context. A single cancel() wakes every select watching ctx.Done(), and all three stop.What-if: ignores ctx

Rust 5 scenarios

Threads

  1. 01Sharing a counter between threads with Arc<Mutex<T>>Two threads add 1 to a shared counter. The second waits in lock() until the first drops its guard.What-if: lock held during work

Async

  1. 02Tokio tasks switch only at .awaitTwo tasks share one thread. They take turns, and every switch happens at an .await.What-if: no .await
  2. 03Blocking in async code stalls every taskmain calls a blocking sleep on the runtime's only thread. The ticker can't tick until it ends.What-if: spawn_blocking, sleep().await
  3. 04join! vs awaiting one by oneEach .await finishes before the next line runs, so fetch("b") starts only after fetch("a") is done.What-if: join!
  4. 05future cannot be sent between threads safely, explainedA spawned task can resume on another worker thread after any .await, so a std MutexGuard has to be dropped first.What-if: tokio::sync::Mutex

Compare across languages

  1. Concurrency vs parallelismTaking turns on one thread vs running at the same instant on two cores, in Rust, Python and free-threaded Python.
  2. Mutex vs semaphoreOne thread at a time vs N at a time, with a Go mutex and Java semaphores of 2 and 1 permits.
  3. Process vs threadThreads share memory and can race on it; processes each get their own copy. Java threads next to Python processes.