Concurrency models define how programs handle multiple tasks simultaneously, whether through parallel execution on multiple cores or interleaved execution on a single core. Different programming languages adopt fundamentally different approaches β message passing (Go, Erlang), shared memory (Java, C++), async/await (JavaScript, Python), and ownership-based (Rust) β each optimized for different classes of problems. The landscape spans from low-level atomic operations and memory ordering to high-level abstractions like actors and CSP channels. Understanding these models is essential because the choice shapes performance, safety, and complexity: a lock-based approach may deadlock where a message-passing design cannot, and an async runtime may outperform threads for I/O-bound work while underperforming for CPU-bound tasks.
What This Cheat Sheet Covers
This topic spans 12 focused tables and 89 indexed concepts, 86 flashcards. Below is a complete table-by-table outline of this topic, spanning foundational concepts through advanced details.
A jump-to index of every table row in this cheat sheet.
An interactive map of every table and concept in this topic.
Table 1: Fundamental Concurrency Models
These are the big-picture paradigms every concurrent system is built on β the mental models you choose between before writing a line of code. They split roughly into how work communicates (actors and CSP pass messages; event loops and async share none) and how work is scheduled (OS threads, green threads, M:N hybrids), and the same language often offers several at once.
| Model | Example | Description | |
|---|---|---|---|
Thread t = new Thread(() -> doWork());t.start(); | β’ OS-managed units of execution β’ threads share memory, processes are isolated β’ threads have lower overhead but require synchronization | ||
case class Message(data: String)actor ! Message("hello") | β’ Isolated actors communicate via asynchronous messages β’ each actor processes messages sequentially β’ popular in Erlang/Elixir/Akka β’ avoids shared state | ||
ch := make(chan int)ch <- 42 | β’ Channels for message passing between goroutines β’ anonymous communication (vs. actor identities) β’ core abstraction in Go and Clojure core.async | ||
async def fetch(): await asyncio.sleep(1) | β’ Cooperative multitasking using async functions that yield at await points β’ single-threaded event loop schedules coroutines β’ ideal for I/O-bound workloads | ||
Fiber.new { sleep 1 }.resume | β’ User-space threads scheduled by runtime (not OS) β’ lower context-switch overhead β’ Ruby fibers and pre-1.2 Go goroutines are examples | ||
N/A (runtime implementation) | β’ M user threads multiplexed onto N OS threads β’ Go's goroutine scheduler (M:N) β’ balances parallelism with low overhead | ||
setTimeout(() => console.log('tick'), 0) | β’ Single-threaded loop polling for events (I/O, timers) β’ JavaScript and Node.js core model β’ non-blocking I/O via callbacks/promises | ||
arr.par_iter().map(|x| x * 2) | β’ Same operation applied across data chunks in parallel β’ GPUs and SIMD β’ Rust Rayon and Python NumPy use this pattern | ||
CompletableFuture.supplyAsync(() -> compute()) | β’ Different operations run concurrently β’ fork-join and thread pools β’ common in Java ExecutorService and C++ std::async |