Concurrency
Parallelism
Computer Science
Programming Concepts
Software Development

What is the difference between concurrency and parallelism?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Concurrency means structuring a program to handle multiple tasks that can make progress independently. Parallelism means executing multiple tasks simultaneously on multiple processors. Concurrency is about design — dealing with many things at once. Parallelism is about execution — doing many things at once. You can have concurrency without parallelism (single-core CPU switching between tasks) and parallelism without concurrency (SIMD processing the same instruction on multiple data points). Rob Pike summarized it as: "Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once."

Concurrency: Managing Multiple Tasks

Concurrency is about program structure. A concurrent program can handle multiple tasks by interleaving their execution, even on a single CPU core.

python
1import asyncio
2
3# Concurrent — two tasks interleave on one thread
4async def download_file(name, seconds):
5    print(f"Start downloading {name}")
6    await asyncio.sleep(seconds)  # Yields control to other tasks
7    print(f"Finished downloading {name}")
8
9async def main():
10    # Both tasks run concurrently (not in parallel)
11    await asyncio.gather(
12        download_file("file_a.zip", 2),
13        download_file("file_b.zip", 3),
14    )
15
16asyncio.run(main())
17# Output:
18# Start downloading file_a.zip
19# Start downloading file_b.zip
20# Finished downloading file_a.zip   (after 2s)
21# Finished downloading file_b.zip   (after 3s)
22# Total time: ~3s (not 5s)

Both downloads make progress concurrently, but only one line of Python executes at any instant (single thread).

Parallelism: Simultaneous Execution

Parallelism requires multiple processors/cores executing tasks at the same physical time.

python
1from multiprocessing import Pool
2import math
3
4# Parallel — truly simultaneous on multiple CPU cores
5def compute_heavy(n):
6    return sum(math.factorial(i) for i in range(n))
7
8if __name__ == "__main__":
9    with Pool(4) as pool:  # 4 worker processes
10        results = pool.map(compute_heavy, [500, 500, 500, 500])
11    # All 4 computations run simultaneously on 4 cores

Each worker process runs on a different CPU core. The computations happen at the same time, not interleaved.

Visual Comparison

 
1CONCURRENCY (1 CPU core):
2Task A: ██░░██░░██████
3Task B: ░░██░░██░░░░░░
4         ↑ tasks interleave on one core
5
6PARALLELISM (2 CPU cores):
7Core 1: ██████████████  (Task A)
8Core 2: ██████████████  (Task B)
9         ↑ tasks run simultaneously
10
11CONCURRENT + PARALLEL (2 cores, 4 tasks):
12Core 1: ██AA░░CC██AA░░CC
13Core 2: ░░BB██DD░░BB██DD
144 tasks interleave across 2 cores

Real-World Analogy

ScenarioType
One cashier serving two lines by alternatingConcurrent, not parallel
Two cashiers each serving their own lineParallel
Two cashiers serving three lines by alternatingConcurrent and parallel
One cashier serving one lineNeither

Concurrency in Different Languages

Go — Goroutines (Concurrent, Optionally Parallel)

go
1func main() {
2    // Goroutines are concurrent — Go runtime schedules them
3    go fetchURL("https://api.example.com/a")
4    go fetchURL("https://api.example.com/b")
5    go fetchURL("https://api.example.com/c")
6    // May run in parallel if GOMAXPROCS > 1
7
8    time.Sleep(5 * time.Second)
9}

Java — Threads (Parallel)

java
1// Java threads run in parallel on multiple cores
2Thread t1 = new Thread(() -> computeHeavy(1));
3Thread t2 = new Thread(() -> computeHeavy(2));
4t1.start();
5t2.start();
6t1.join();
7t2.join();

JavaScript — Event Loop (Concurrent, Not Parallel)

javascript
1// JavaScript is single-threaded — concurrent via event loop
2fetch("https://api.example.com/a").then(handleA);
3fetch("https://api.example.com/b").then(handleB);
4// Both requests are in-flight concurrently
5// But callbacks execute one at a time on the event loop

When to Use Each

ScenarioUseWhy
Web server handling requestsConcurrencyI/O-bound, need to handle many connections
Image processing pipelineParallelismCPU-bound, benefit from multiple cores
Database query + API callConcurrencyBoth are I/O-bound, interleave waiting
Matrix multiplicationParallelismCPU-bound, split across cores
Chat applicationConcurrencyMany idle connections, little CPU per message
Video encodingParallelismCPU-intensive, split frames across cores

Python's GIL and the Distinction

python
1import threading
2import multiprocessing
3
4# Threading — concurrent but NOT parallel for CPU work (GIL)
5# Good for I/O-bound tasks
6threads = [threading.Thread(target=io_task) for _ in range(4)]
7
8# Multiprocessing — truly parallel (separate processes, no GIL)
9# Good for CPU-bound tasks
10processes = [multiprocessing.Process(target=cpu_task) for _ in range(4)]

Python's Global Interpreter Lock (GIL) prevents multiple threads from executing Python bytecode simultaneously. Threads are concurrent but not parallel for CPU-bound work. Use multiprocessing for true parallelism.

Common Pitfalls

  • Assuming concurrency requires multiple cores: Concurrency is a software design concept. A single-core CPU can run concurrent programs by time-slicing between tasks. Parallelism requires hardware support (multiple cores or processors).
  • Using threads for I/O-bound work when async is simpler: Thread-based concurrency adds complexity (locks, race conditions). For I/O-bound tasks (HTTP requests, file reads, database queries), async/await provides concurrency with less overhead and no shared-state bugs.
  • Ignoring the GIL in Python: Python threads do not speed up CPU-bound work due to the GIL. Using threading for matrix multiplication gives no speedup. Use multiprocessing or libraries like NumPy that release the GIL during computation.
  • Assuming parallel code is always faster: Parallelism has overhead — process creation, memory copying, synchronization. For small tasks, the overhead exceeds the time saved. Parallelize only when the work is large enough to amortize the startup cost.
  • Confusing async with parallel: async/await in JavaScript and Python provides concurrency (interleaving) not parallelism (simultaneous execution). An await fetch() call yields control to other tasks but does not run on a separate core.

Summary

  • Concurrency: Structuring code to handle multiple tasks that make independent progress (design)
  • Parallelism: Executing multiple tasks simultaneously on multiple cores (execution)
  • Concurrency without parallelism: single-core time-slicing (async I/O, event loops)
  • Parallelism without concurrency: SIMD, GPU shader programs
  • Use concurrency for I/O-bound work (web servers, network requests)
  • Use parallelism for CPU-bound work (image processing, scientific computing)
  • Python GIL: threads are concurrent but not parallel for CPU work; use multiprocessing

Course illustration
Course illustration

All Rights Reserved.