AlgoMaster Logo

Concurrency vs Parallelism

Medium Priority14 min readUpdated July 4, 2026
AI Mock Interview

Practice this topic in a realistic system design interview

Listen to this chapter
Unlock Audio

Concurrency and parallelism are often used as if they mean the same thing. They do not.

They solve different problems, and mixing them up leads to the wrong fix. You might add more threads when the database is the real bottleneck, or add more machines when the service mostly needs to stop waiting around.

The difference comes down to whether work overlaps in time or truly runs at the same instant.

Concurrency means a system can keep many tasks in progress during the same period of time. The tasks take turns, pause, resume, and overlap. One task does not need to finish before another one starts.

Parallelism means multiple tasks are actually executing at the same instant, usually on different CPU cores, machines, or GPUs.

Loading simulation...

A web server holding 10,000 open connections is concurrent. Most of those connections may be waiting on the network or a database.

A data job using 64 CPU cores to transform records at the same time is parallel. Real computation is happening on many cores at once.

The trade-offs are different too. Concurrency is mostly about keeping a system responsive while work waits on I/O. Parallelism is mostly about using more compute power to finish heavy work faster.

Most real systems use both.

This chapter covers how concurrency and parallelism differ, their separate trade-offs, and where each applies.

1. Concurrency

Concurrency means a system can keep more than one task in progress during the same period of time.

The tasks do not have to run at the exact same instant. A single CPU core can still handle concurrent work by switching between tasks.

For example, an event loop can start a database call for one request, move on to another request while the database is working, and come back when the result arrives.

That is the heart of concurrency: do not sit idle while one task is waiting.

Run a little workWait for databaseRun a little workWait for networkRun a little workWait for diskResume when database returnsSingle CPU CoreRequest ARequest BRequest CSingle CPU CoreRequest ARequest BRequest C
7 / 7
algomaster.io

The server is not doing everything at once. It is keeping several operations alive and spending time on whichever one can move forward.

What Concurrency Buys You

Most backend services spend a surprising amount of time waiting. They wait on databases, caches, remote APIs, disk, network packets, locks, model calls, and other services.

If one request freezes the whole process while it waits, the service wastes capacity. Concurrency lets the system handle other work during those gaps.

This is why concurrency matters so much in web servers, API gateways, message consumers, crawlers, chat systems, and workflow services.

Common Concurrency Models

Concurrency can be built in several ways. The names vary by language, but the ideas repeat.

The thread-per-request model gives each request its own operating system thread. This keeps blocking code easy to read, but it gets expensive when thousands of threads consume memory and force the operating system to switch between them.

A thread pool uses a fixed number of threads to run many tasks. This works well for web servers, workers, and database clients. The trade-off is that once the pool is full, new work has to wait in a queue.

An event loop uses one or a few threads to coordinate many I/O operations. This is a strong fit for high connection counts. The catch is simple: if you run CPU-heavy work on the loop, every other request waits.

Async/await lets code pause while waiting for I/O and resume later. It is useful when an API calls many other services. It also needs care around timeouts, cancellation, and work that accidentally keeps running after the caller has gone away.

The actor model keeps state inside small units called actors. Actors talk by sending messages. This can make shared state safer, but message order and growing mailboxes still need attention.

Queue workers pull jobs from a queue. They are great for background work and retries, but a growing backlog is a sign that the system is falling behind.

The important point: concurrency is not the same as creating many threads. Threads are one tool. Async I/O, event loops, actors, fibers, coroutines, and queues are also concurrency tools.

Example: Concurrent Requests

A service receives three requests. Each request needs a database call that takes 100 ms.

Sequential handling:

Concurrent handling:

Concurrency improves latency and throughput when work spends time waiting. It does not make one CPU core execute more instructions per second.

Concurrency Costs

Concurrency adds new ways to fail.

A race condition happens when two tasks touch shared state and the final result depends on timing. A deadlock happens when tasks wait forever for each other to release locks.

Too much concurrency can also exhaust memory, sockets, threads, or database connections.

Thread-heavy systems can waste CPU just switching between threads instead of doing useful work.

Another common problem is overload. If a service accepts more work than its database or downstream APIs can handle, latency rises and failures spread.

Debugging is harder too, because timing changes from run to run. A bug may disappear when you add logging or run the test again.

Good concurrent systems use limits: connection pools, worker pools, queues, semaphores, rate limits, timeouts, and cancellation.

2. Parallelism

Parallelism means multiple pieces of work are executing at the same instant.

Parallelism needs more than one place to run work: CPU cores, machines, GPU cores, or other accelerators.

It helps most when the work is heavy and can be split into mostly independent pieces.

This is the basic shape behind many familiar workloads: summing a large array across several CPU cores, encoding multiple video frames at the same time, or training a model on multiple GPUs.

The same idea shows up when a cluster runs many simulations, Spark processes many dataset partitions, or several model replicas serve inference at the same time.

Parallelism helps when the time saved is larger than the cost of splitting the work, scheduling it, and combining the results.

Parallelism Is Not Free

Adding more cores does not automatically make work faster.

There is overhead at every step: splitting work into chunks, scheduling tasks, moving data around, coordinating shared state, combining results, and waiting for the slowest piece to finish.

Some workloads do not split well. If 90% of a job can run in parallel but 10% must run one step at a time, that 10% becomes the limit.

In distributed systems, the network or storage often becomes the bottleneck before the CPU does.

Example: Parallel Sum

This example splits an array into smaller ranges and sums those ranges in parallel. Each language uses a common tool for parallel work: a fork/join pool, goroutines, tasks, futures, or worker processes.

The result is 5050.

For an array of 100 integers, this is overkill. The overhead is larger than the benefit. Parallelism pays off when the work per chunk is large enough.

3. Concurrency vs Parallelism in Services

In system design, concurrency and parallelism often show up in different parts of the same system.

An API server is concurrent when it keeps many requests in progress while they wait on I/O. It is parallel when several request handlers run on different CPU cores at the same instant.

A database client is concurrent when it has many queries waiting for responses. The database engine may be parallel when it scans partitions using several internal workers.

Queue workers are concurrent when many jobs are reserved or waiting on external services. They are parallel when many workers process jobs at the same time across cores or machines.

Data pipelines show the same split. Several stages can be active at once, while Spark tasks run in parallel across many executors.

AI systems may hold many conversations waiting on retrieval or tool calls. That is concurrency. At the same time, GPU batching or multiple model replicas may run work in parallel.

Even a browser coordinates UI events, network requests, timers, and rendering concurrently, while image decoding, rendering work, and web workers may use multiple cores in parallel.

A busy backend usually needs three things together:

  1. Concurrency, so many requests and I/O operations can stay in progress.
  2. Parallelism, so the system can use available cores and machines.
  3. Backpressure, meaning limits that slow or reject extra work before databases, queues, and downstream APIs are overwhelmed.

4. Four Common Combinations

Concurrency and parallelism are separate ideas. A system can have either one, both, or neither.

Seeing the four combinations side by side makes the difference much easier to remember.

Neither Concurrent Nor Parallel

One task runs to completion before the next starts.

This is simple and predictable. It is also slow when tasks wait on I/O or when CPU work could be split up.

Concurrent, Not Parallel

Multiple tasks are in progress, but only one is executing at any exact moment.

An event loop running on one thread and handling many socket connections is a typical example.

This is good for I/O-heavy workloads. CPU-heavy code can still block the whole loop.

Parallel, Not Concurrent

One job is split into pieces, and those pieces execute at the same time.

A batch job that partitions one dataset and processes all partitions at once is a typical example.

There may be only one user-visible job, but inside that job, many pieces run at once.

Concurrent and Parallel

Many tasks are in progress, and several are executing at the same instant.

This is the normal shape of modern backend services: many requests in progress, many cores active, and many dependencies being waited on.

5. Choosing Between Concurrency and Parallelism

Use concurrency when the system spends a lot of time waiting.

API servers handling many clients, WebSocket gateways, and crawlers fetching many URLs all fit this pattern.

So do services that call several downstream APIs, queue consumers that wait on databases or third-party APIs, and AI agents that wait on retrieval, tools, or model responses.

The common thread is time spent blocked on something external.

Use parallelism when the system does a lot of computation.

Image and video processing, compression, encryption, search indexing, analytics, ETL jobs, machine learning training, batch inference, embedding generation, and scientific simulations all fit this pattern.

The common thread is real compute work, with enough work per chunk to make splitting it worthwhile.

A useful way to choose is to start with the bottleneck.

If the system is waiting on the network or a database, the first move is usually more concurrency with sensible limits.

If one CPU core is maxed out, the first move is usually parallelism across cores. If a GPU is sitting idle, batching or running more work on that device can help.

If the database itself is overloaded, more concurrency makes things worse. The fix is usually less concurrency, better queries, caching, or backpressure that slows callers down.

If thread counts are out of control, limited-size pools or async I/O are the fix. If a queue backlog is growing, more workers help only when the services they call still have capacity.

That last point matters. More concurrency is not always better. If the database is already overloaded, adding more concurrent requests only makes latency worse. Good systems use limits to protect shared dependencies.

6. Common Mistakes

Most failures come from treating concurrency and parallelism as the same thing.

Two common examples:

  1. Adding workers when the real bottleneck is a shared database.
  2. Running blocking or CPU-heavy work in a place that was designed to pause and resume quickly.

Mistake 1: Treating Threads as Free

Every thread consumes memory. Every thread also adds scheduling cost.

Thousands of blocked threads can make a system spend too much time switching between threads and too little time doing useful work.

Use limited-size pools, async I/O, or event-driven designs when connection counts are high.

Mistake 2: Running CPU Work on an Event Loop

An event loop is good at coordinating I/O. It is bad at running long CPU-heavy tasks directly on the loop.

If a Node.js server, Python async service, or browser main thread runs expensive computation on the event loop, it stops processing other events. Move that work to worker threads, processes, or a separate service.

Mistake 3: Ignoring Shared State

Concurrency means tasks can overlap in many different orders. If two tasks update shared state, the order matters.

Use transactions, locks, atomic operations, message passing, immutable data, or single-writer designs where appropriate.

Mistake 4: Confusing More Workers With More Throughput

Workers increase throughput only until another bottleneck appears.

If every worker calls the same database, external API, or GPU service, the downstream system sets the real limit.

Mistake 5: Forgetting Cancellation and Timeouts

Concurrent systems need cancellation. If a client disconnects or a request times out, expensive downstream work should stop when possible.

This is especially important for AI workloads. A model should not keep generating tokens for a user who has already closed the page.

Summary

Concurrency and parallelism solve different problems.

Concurrency is about keeping many tasks in progress at once. It helps systems stay responsive while work waits on I/O, and it does not require multiple cores.

It is usually built with async I/O, event loops, threads, and queues. It fits I/O-heavy workloads such as an API server handling many open requests. Its main risks are race conditions, overload, and running out of resources.

Parallelism is about multiple tasks executing at the same instant. It helps compute-heavy work finish faster, and it does require multiple cores, machines, or accelerators.

It shows up in multi-core code, distributed workers, and GPUs. It fits CPU- or GPU-heavy workloads such as a Spark job processing partitions across executors. Its main costs are coordination, uneven work distribution, and waiting for shared state safely.

A scalable service usually needs both. It also needs limits. Unlimited concurrency and careless parallelism create overload, not capacity.

Quiz

Concurrency vs Parallelism Quiz

10 quizzes