Concurrency vs Parallelism: The Actual Difference
Concurrency is about dealing with multiple things at once — managing their structure and interleaving. Parallelism is about doing multiple things at once — physically simultaneous execution. They are independent axes, not synonyms.
- Estimated time
- ~10 min
- Difficulty
- intermediate
- Sources
- 5 sources
Your web server is handling 10,000 simultaneous requests right now — but it might be running on a single CPU core. How is that possible? And when you do add more cores, what exactly changes?
The barista analogy: one person, many orders
Picture a coffee shop with a single barista. Three customers order drinks at once.
The barista doesn’t clone herself. She starts brewing drink A, and while it drips she steams milk for drink B, and while the steam wand heats she labels a cup for drink C. She interleaves small steps across all three orders.
All three customers see progress. None of them are waiting idle. But at any instant, only one thing is physically happening — the barista’s two hands are doing exactly one thing.
Now imagine a second barista walks in. Suddenly two drinks are being made at the same moment. The wall-clock time to serve all three customers genuinely shrinks.
The first scenario is concurrency. The second is parallelism. They feel similar from the outside — work gets done — but the underlying mechanism is completely different.
What the simulator shows
One worker alternates between Task A and Task B in time slices. The total work is always 40 units (20 + 20), so wall-clock time is always 40 regardless of how finely you slice it. Concurrency gives the appearance of simultaneity without actually shrinking total compute time.
Check your understanding
A barista starts all three drinks before finishing any one of them. Which property does this demonstrate?
The formal distinction
A program is concurrent if it is structured to handle multiple tasks whose progress can interleave — even on a single processor. Concurrency is a property of the program’s design.
A program is parallel if multiple computations physically execute at the same instant on different hardware units. Parallelism is a property of execution, not design.
The classic phrasing is Rob Pike’s: “Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.”
This is not wordplay. It has engineering consequences:
- A concurrent single-threaded server (Node.js, Python asyncio) can handle thousands of clients — because it never blocks waiting for one client when another has data ready.
- A parallel but non-concurrent program (a batch SIMD operation) can crunch 16 floats at once, but can only do one kind of task at a time.
- Most production systems are both: Go’s goroutine runtime, multi-threaded Java servers, modern OS kernels.
Show the formal model: processes, interleavings, and the happens-before relation
Formally, a concurrent system is modelled as a set of processes where each executes a sequence of events. Two events and from different processes are concurrent if neither nor in the happens-before order (Lamport, 1978).
[Time, Clocks, and the Ordering of Events in a Distributed System]Parallelism maps to the physical realisation: if a hardware scheduler assigns to core and to core during the same wall-clock interval , then and execute in parallel during that interval.
A set of events can be logically concurrent (no ordering relation) without being physically parallel (same core at any instant). This is the formal version of “concurrency ≠ parallelism.”
[The Art of Multiprocessor Programming]Check your understanding
Python's asyncio event loop runs on a single OS thread due to the GIL. Can it still be called concurrent?
Why adding workers actually helps
Now try adding more workers to the same pool of tasks.
What the parallel workers simulation shows
With 1 worker, 8 tasks take 80 units sequentially. With 2 workers, tasks are split 4-and-4 and complete in 40 units — a 2× speedup. With 8 workers, each runs its own task simultaneously, completing in 10 units — 8× speedup. This is real parallelism: simultaneous physical execution reduces wall-clock time.
Notice what changes and what doesn’t:
- Total CPU work is constant — 80 units regardless of worker count. You aren’t doing less work; you’re doing it at the same time on different hardware.
- Wall-clock time drops proportionally (up to the task-count limit). This is the speedup concurrency alone can never give you.
- Beyond task count, adding workers stops helping. Eight tasks can’t benefit from nine workers — one worker would sit idle.
This ceiling is captured by Amdahl’s Law: the maximum speedup of a parallel program is bounded by its serial fraction.
Where is the fraction of the program that can be parallelised and is the number of processors. As , .
[Computer Systems: A Programmer's Perspective (CSAPP)]Check your understanding
You have a program that is 80% parallelisable. According to Amdahl's Law, what is the maximum speedup, regardless of how many cores you throw at it?
The two-axis map: where real systems live
Concurrency and parallelism are independent axes, not a single spectrum from “slow” to “fast.” A system can be:
| System type | Concurrent? | Parallel? | Canonical example | |
|---|---|---|---|---|
| Sequential | No | No | A simple bash script, single-core single-task | |
| Concurrent, not parallel | Yes | No | Node.js event loop, Python asyncio, OS scheduler on 1 core | |
| Parallel, not concurrent | No | Yes | GPU SIMD — same instruction on thousands of data lanes | |
| Concurrent and parallel | Yes | Yes | Go runtime, multi-threaded JVM servers, Linux kernel |
The four quadrants explained
Sequential systems (bash script) sit at the origin. Node.js lives in the top-right of the concurrent axis but low on parallel. GPU shader cores live at the top of the parallel axis with low concurrency. Go’s runtime and modern OS kernels occupy the top-right — both highly concurrent and parallel.
Common misconception
Concurrency and parallelism are the same thing — both mean 'running at the same time.'
What's actually true
Concurrency is a design property: tasks are structured to make progress without blocking each other. Parallelism is a hardware property: multiple execution units advance simultaneously. Node.js is concurrent but never parallel (one thread). A GPU doing matrix multiplication is parallel but barely concurrent (same instruction everywhere). The two can exist independently.
Check your understanding
A GPU runs 4,096 shader cores, each executing the same instruction on a different pixel. How would you classify this?
Where the model breaks (and what to do about it)
Concurrency and parallelism each have hard edges worth knowing.
Shared state is the enemy of both. Concurrency alone doesn’t cause race conditions — sequential task-switching on one core is safe if you never share mutable state. But the moment concurrent design meets parallel execution, any unguarded shared variable becomes a data race.
I/O concurrency doesn’t help CPU-bound work. Node.js handles 10,000 simultaneous HTTP connections because most of them are waiting for the network. If you try to use it for CPU-intensive image processing, all 10,000 connections stall while the one CPU-heavy operation runs. Concurrency buys you overlap of waiting, not of computing.
Parallelism doesn’t help I/O-bound work. Adding 64 cores to a system that spends 95% of its time waiting for a database doesn’t make the database answer faster. The bottleneck is latency, not compute. This is why web servers need concurrency (to overlap I/O waits) more than they need raw parallelism.
Amdahl’s wall. The serial fractions of real programs are hard to eliminate. Lock contention, startup costs, and coordination overhead all eat into the parallelisable fraction . In practice, real-world speedups flatten far below the theoretical limit.
Rule of thumb
Use concurrency to handle many things that spend time waiting. Use parallelism to speed up things that spend time computing. When you need both, design the concurrency structure first — then map it onto parallel hardware.
Rob Pike’s formulation is especially useful here: “Concurrency enables parallelism.” A well-decomposed concurrent program (separate tasks, no shared mutable state) maps naturally onto parallel hardware. A sequential monolith, no matter how many cores you throw at it, stays sequential.
Check your understanding
A video transcoder is CPU-bound and uses all 8 cores. Adding I/O-concurrency (async file reads) is unlikely to help because...
Check your understandingQ 1 / 5
Which statement best captures Rob Pike's distinction?