AlgoInsight

Free lesson · Concurrency Interview Mastery

Two words people use interchangeably - and shouldn't

Easy10 min readconcurrencymultithreadinginterview-prep

In the last lesson I told you concurrency is about interleaving, not simultaneity. Now I have to complicate that slightly, because sometimes things are simultaneous - and the word for that is parallelism, not concurrency. These two words get thrown around as synonyms constantly, including by working engineers, and an interviewer asking you to distinguish them is checking whether you're one of the people who actually knows the difference.

Here's the cleanest way I know to hold them apart:

Concurrency is about dealing with many things at once. Parallelism is about doing many things at once.

Concurrency is a property of how your program is structured - it's designed so multiple tasks are in flight and can make progress over the same stretch of time. Parallelism is a property of how your program is executed - multiple tasks are literally running in the same instant, on different hardware.

The reason this matters: a program can be concurrent without being parallel, and the distinction has real consequences for how you reason about it.

One kitchen, one cook vs. one kitchen, many cooks

Let me give you the analogy I use, because it makes the difference physical.

Imagine a single cook making breakfast: eggs, toast, coffee. A good cook doesn't make them one-at-a-time, fully finishing the eggs before touching the toast. They start the coffee brewing, drop the bread in the toaster, and while both of those are working on their own, they crack the eggs. One cook, but three dishes all making progress. That's concurrency - one worker juggling multiple tasks by switching attention whenever one is waiting (the coffee brewing, the toast toasting).

Now add a second and third cook. Cook one does eggs, cook two does toast, cook three does coffee, all at literally the same moment. That's parallelism - multiple workers genuinely doing things simultaneously.

See the difference? Concurrency is a structuring decision (one cook, smart juggling). Parallelism is an execution reality (multiple cooks, real simultaneity). And here's the kicker that ties back to hardware: the number of cooks is your number of CPU cores.

flowchart TD
    subgraph Concurrent["Concurrent, NOT Parallel (1 core)"]
        C1[Core] --> T1[Task A: slice]
        C1 --> T2[Task B: slice]
        C1 --> T3[Task A: slice]
        C1 --> T4[Task B: slice]
    end
    subgraph Parallel["Concurrent AND Parallel (2 cores)"]
        P1[Core 1] --> PA[Task A runs]
        P2[Core 2] --> PB[Task B runs]
    end
This diagram contrasts two situations. On the left, labeled concurrent but not parallel, a single core handles two tasks A and B by running a slice of A, then a slice of B, then back to A, then B - interleaving them over time on one core. On the right, labeled concurrent and parallel, there are two cores: core one runs task A while core two runs task B, both genuinely at the same time. The left side shows juggling on one core; the right side shows true simultaneous execution across two cores.

Parallelism is one way to execute concurrency

Now the relationship between them, which is the part people get backwards.

Concurrency is the broader idea. It says "this program is organized into tasks that can make progress independently." How those tasks actually run is a separate question. If you have one core, the runtime interleaves them - concurrent, not parallel. If you have many cores, the runtime can spread them out - concurrent and parallel. Same concurrent program, two different executions depending on the hardware underneath.

So parallelism is not the opposite of concurrency, and it's not a competing approach. Parallelism is one possible way to execute a concurrent design. You write concurrent code; the system decides how much of it actually runs in parallel based on the cores available.

Note: This is why the same concurrent program can run on your laptop and on a 64-core server and behave correctly on both - it just gets more actual parallelism on the big machine. You designed for concurrency; the hardware supplied as much parallelism as it had. This separation is a feature, not an accident.

And the reverse insight matters too: you can have parallelism without writing what we'd call concurrent code in the threading sense - for example, a numeric library splitting a giant matrix multiply across cores. But in interview contexts, the framing that matters is the one above: concurrency is the design, parallelism is one way it executes.

Why the distinction has teeth

This isn't pedantry. The distinction changes what you're optimizing for and what can go wrong.

If your problem is responsiveness - keeping a UI alive while work happens, serving many requests that mostly wait on I/O - you need concurrency. You might not need a single extra core. A server handling ten thousand mostly-waiting connections does it with concurrency; the tasks spend their lives blocked on the network, so one or a few cores juggling them is plenty.

If your problem is raw throughput on heavy computation - crunching a massive dataset, training a model, rendering frames - you need parallelism. Juggling won't help; the work is CPU-bound, there's no waiting time to fill, so the only way to go faster is more cores actually running at once.

Tip: A quick diagnostic: is your task mostly waiting (network, disk, database) or mostly computing (math, processing)? Waiting-heavy work benefits from concurrency - you fill the idle time. Compute-heavy work benefits from parallelism - you need more cores genuinely running. Conflate the two and you'll throw cores at a waiting problem (no help) or threads at a compute problem on one core (no help). Matching the tool to the bottleneck is the senior move.

There's a famous one-liner from the Go community - concurrency is not parallelism - and the deeper point behind it is exactly this: concurrency is a way of structuring a solution into independent pieces, and whether those pieces run in parallel is a separate, lower-level concern. Design the structure first; let the parallelism follow from the hardware.

Two jobs, two right answers

Let me make the "match the tool to the bottleneck" idea concrete with two jobs, because seeing where each one's time actually goes is what makes the distinction click.

Job one: a chat server holding 50,000 open connections. Where does its time go? Almost entirely waiting - for the next message to arrive on each socket. At any instant, 49,900 of those connections are idle, just held open. The CPU work per message is tiny; the waiting is enormous. This is the textbook case for concurrency: you want one or a few cores juggling tens of thousands of mostly-idle connections, switching to whichever one just got data. Throwing 64 cores at this barely helps, because the bottleneck was never CPU - it was the sheer number of things to wait on. The right design is concurrent (lots of tasks in flight) and barely parallel (few cores busy).

Job two: resizing 50,000 high-resolution photos. Where does its time go? Almost entirely computing - every pixel of every image needs arithmetic. There's essentially no waiting to fill. Juggling on one core does nothing here; the core is already pinned at 100%. The only way to go faster is more cores genuinely working at once. This is the textbook case for parallelism: split the images across all available cores and run them truly simultaneously. Eight cores ≈ eight times faster, roughly.

Example: Same word - "fast" - two completely different meanings. For the chat server, fast means responsive: every connection gets attention promptly even though little CPU is used. For the photo job, fast means high throughput: maximum pixels processed per second by saturating the cores. Concurrency delivers the first; parallelism delivers the second. Reach for the wrong one and you've optimized a dimension that wasn't your bottleneck.

Common misconceptions to clear out

A few wrong beliefs that this distinction quietly fixes, worth stating plainly so they don't trip you later:

Tip: One more vocabulary pair you may hear: task parallelism (different cores doing different operations - one resizes, one encodes, one uploads) versus data parallelism (different cores doing the same operation on different chunks of data - every core resizes its slice of the photos). Both are forms of parallelism; the distinction is whether the cores are running different code or the same code on different data. It occasionally comes up as a follow-up, and naming it correctly is a nice signal.

Most real systems are both at once

Here's something the clean examples can hide: most real software isn't purely I/O-bound or purely CPU-bound. It's a mix, and the skill is decomposing it so each part gets the right treatment.

Take a single web request that uploads a photo, runs it through an image filter, and saves the result. Walk its phases: receiving the upload over the network is I/O-bound (mostly waiting). Applying the filter is CPU-bound (mostly computing). Writing to storage is I/O-bound again. One request, three phases, two different bottlenecks.

A well-built server handles this with both tools layered together. At the top level, it uses concurrency to juggle thousands of these requests - while one request waits on its upload or its database write, the server serves others. That keeps it responsive under load. But for the CPU-bound filter step specifically, it may hand that chunk of work to a pool of threads running across multiple cores - parallelism - so a heavy filter on one request doesn't stall everything behind it.

Example: The pattern to internalize: concurrency at the I/O boundaries, parallelism at the compute cores. The waiting parts (network, disk, database) want concurrency so you fill idle time and stay responsive. The crunching parts (image processing, encryption, math) want parallelism so you actually use the cores. A senior engineer designs the system so each kind of work flows to the tool that fits it, rather than picking one model for the whole program.

This is also why "is my system concurrent or parallel?" is usually the wrong question for real software. The right question is "where does this system wait, and where does it compute?" - because the waiting parts and the computing parts want different things, and a good design serves both.

The interview angle

Interviewers ask "what's the difference between concurrency and parallelism?" constantly, precisely because so many candidates fumble it. The fumble is usually one of two things: treating them as identical, or claiming they're opposites. Both are wrong, and both are easy to avoid.

Interview: A crisp answer: "Concurrency is about dealing with many tasks at once - it's how I structure the program so tasks can make progress independently. Parallelism is about doing many tasks at once - actually executing them simultaneously on multiple cores. Concurrency is the design; parallelism is one way that design gets executed when there's hardware for it. A concurrent program runs on one core by interleaving and on many cores by actually running in parallel - same code, different execution."

Be ready for the follow-up: "Can you have one without the other?" Yes - concurrency without parallelism is a juggling single core (one cook, many dishes). Parallelism is the execution side that kicks in when you have multiple cores. The framing interviewers want is that concurrency is the broader structuring concept and parallelism is the simultaneous-execution payoff.

Bonus signal: tie it to the problem type. "If I'm I/O-bound and need responsiveness, I reach for concurrency. If I'm CPU-bound and need throughput, I need real parallelism." That last move - connecting the definitions to when you'd use each - is what makes the answer sound like it came from someone who's actually shipped both.

A subtlety worth carrying

One thing that confuses people: on a multi-core machine, your concurrent program is both concurrent and parallel at the same time, and that's fine. The tasks are interleaved and some of them genuinely run at once. These aren't mutually exclusive states - parallelism is just the bonus you get when concurrency meets enough cores. Don't tie yourself in knots trying to label a program as "one or the other." Ask instead: is it designed concurrently (multiple independent tasks)? And separately: is it executing in parallel (multiple cores active)? Two questions, two answers.

A gut-check you can run in seconds

When you're staring at a problem and need to decide which way to lean, run this quick mental check. It's the compressed version of everything above.

First, ask: where does the time go? If your task spends most of its life waiting - on the network, a disk, a database, a user - your bottleneck is waiting, and the fix is concurrency: structure the work so the CPU fills idle time with other tasks. If your task spends most of its life computing - math, transforms, encryption - your bottleneck is CPU, and the fix is parallelism: spread the work across more cores actually running at once.

Second, ask: what does "faster" even mean here? If "faster" means staying responsive while many things are in flight, that's a concurrency goal. If "faster" means finishing a fixed pile of computation in less wall-clock time, that's a parallelism goal.

Third, remember the answer is often both, in different places - concurrency to juggle the waiting parts, parallelism to crush the computing parts. You're not picking a team for the whole program; you're routing each kind of work to the model that fits it.

Tip: If you can only remember one sentence from this lesson, make it this: concurrency is a way to structure work; parallelism is a way to execute it. Everything else - the cooks, the cores, the I/O-vs-CPU split - is commentary on that one distinction. Say that sentence in an interview and you've already cleared the bar most candidates trip over.

What you've got now

You can now separate the design question from the execution question - concurrency is how you structure work into independent tasks, parallelism is the simultaneous execution you get when the hardware allows. You know that parallelism is one way to run a concurrent program, not a rival to it. And you know the practical tell: waiting-bound problems want concurrency, compute-bound problems want parallelism.

Next we go one level down into the machinery: the two things you can actually spin up to get concurrency - processes and threads - and why the choice between them shapes everything about how your concurrent code behaves, especially where the bugs live.

Back to Concurrency Interview Mastery