system-design · beginner
Process vs Thread — Isolation and Shared Memory
Start here
When software “runs,” the operating system does not only store files on disk. It runs units of execution.
Two units you will hear constantly:
- Process — a running program with its own virtual memory (its own address space).
- Thread — a sequence of executing instructions inside a process. Multiple threads in one process share the process’s memory by default.
- Do we isolate work in separate processes (safer when one crashes)?
- Or share memory with threads (faster communication, harder correctness)?
What you will learn
- Define process and thread in plain English.
- Explain address space and why isolation matters.
- Contrast crash behavior: one thread fault vs one process fault.
- Explain why threads need locks (and what a race condition is).
- Compare multi-process vs multi-threaded server designs at a beginner level.
- Meet concurrency vs parallelism gently.
- Preview virtual threads / async models as “not classic OS threads only.”
- Practice choosing a model for simple scenarios.
What you should know first
| Idea | Level needed |
|---|---|
| A program is code that runs and uses memory | Basic |
| A server handles many users | Basic intuition |
No prior OS course required.
Words you need before we begin
| Term | Plain English |
|---|---|
| Operating system (OS) | Software that manages hardware and running programs (Linux, Windows, macOS). |
| Process | A running instance of a program with its own memory space and OS resources. |
| Thread | An execution path inside a process; shares the process heap with other threads. |
| Address space | The process’s map of memory addresses it is allowed to use. |
| Heap | Area of memory for objects/data created while the program runs (simplified). |
| Stack | Per-thread memory for function call state (simplified). |
| Context switch | OS pausing one execution unit and running another. |
| Race condition | Outcome depends on unpredictable timing of concurrent access to shared data. |
| Lock / mutex | Mechanism so only one thread at a time runs a critical section. |
| Concurrency | Dealing with many tasks in progress (structure of overlapping work). |
| Parallelism | Actually executing multiple tasks at the same wall-clock time (multi-core). |
Simple story: offices and workers
Process ≈ an office floor with a locked door and its own filing cabinets.
People inside share those cabinets. People in another office cannot open them without going through official channels (like network calls or files).
Thread ≈ a worker inside that office.
Workers can all grab the same shared whiteboard (shared memory). That is fast — and chaotic if two write different numbers at once.
If one office burns down (process crash), other offices may continue.
If one worker knocks over the shared ink bottle across the whiteboard (memory corruption / bad shared state), every worker in that office is affected.
Where the analogy stops: real OS details include file handles, permissions, and CPU scheduling that offices do not have.
The problem these concepts solve
One user, one loop — not enough
A naïve server might:
while true:
accept connection
handle request completely
Only one request at a time. Under two users, the second waits. Throughput is terrible.
To serve many users you need overlapping work. Processes and threads (and later async event loops) are tools for that overlap.
Step-by-step explanation
Step 1 — A process is a running program with walls
When you start an app, the OS creates a process:
- Loads code
- Gives it a private address space
- Tracks open files, security identity, and other resources
Step 2 — A thread is a worker inside the walls
A process starts with at least one thread (the main thread). Programs can create more threads.
All threads in the process typically share:
- Global variables / heap objects
- Open file handles (depending on OS details)
- The code of the program
Step 3 — Why threads exist
Creating a brand-new process for every tiny task is heavier:
- More memory for separate address spaces
- Slower startup
- Communication between processes needs pipes, sockets, or shared memory set up explicitly
Step 4 — Crashes and isolation
| Event | Typical effect |
|---|---|
| Thread throws a handled error | Other threads may continue if the program is careful |
| Bug corrupts shared heap | Whole process becomes unsafe |
| Process crashes / is killed | All threads in it die; other processes can live |
| Separate process peer dies | Survivors continue; must detect and restart peer |
Browsers use multiple processes partly so one bad tab is less likely to kill the entire browser. Many application servers use multiple processes for similar isolation.
Step 5 — Shared memory needs synchronization
Two threads update balance at once:
- Both read
balance = 100 - Both add 20
- Both write
120
A lock (mutex) makes a critical section run with mutual exclusion: only one thread at a time. Locks fix races but can create:
- Deadlocks (everyone waits for everyone)
- Contention (threads serialize; latency tails rise)
Step 6 — Concurrency vs parallelism (short)
- Concurrency: the system is structured to make progress on many tasks (interleaving on one core still counts).
- Parallelism: tasks run at the same time on multiple cores/CPUs.
Step 7 — Server design sketches
Multi-process server (simplified):
- Parent accepts connections or workers pull jobs
- Each process handles requests with isolation
- Failure of one worker is recoverable
- Memory not shared → use DB/cache for shared state
- One process, thread pool
- Shared connection pools and caches in memory
- Risk of races; careful engineering required
- One fatal native crash can kill the whole pool
- One or few threads handle many connections via non-blocking I/O
- Not the same as “one OS thread per request”
- Great for many waiting-on-network tasks; CPU-heavy work still needs care
Step 8 — Language runtimes add more layers
Examples you will meet later:
- Java platform threads map closely to OS threads (simplified).
- Virtual threads (newer Java) are lightweight concurrency managed by the runtime on top of fewer OS threads.
- JavaScript (Node) often uses one main thread + event loop + some worker threads.
- Python GIL (Global Interpreter Lock) affects how CPU-bound threads behave in CPython — processes often used for CPU parallelism.
Visual mental model
Process isolation
flowchart TB
subgraph P1 [Process A]
T1[Thread A1]
T2[Thread A2]
M1[Memory A]
T1 --> M1
T2 --> M1
end
subgraph P2 [Process B]
T3[Thread B1]
M2[Memory B]
T3 --> M2
end
P1 -.->|no direct shared heap| P2
Learning question: Can Thread A1 normally write Memory B by accident?
Caption: Separate processes keep separate memory walls.
Threads sharing a heap
flowchart LR
T1[Thread 1] --> H[Shared heap object]
T2[Thread 2] --> H
T1 -->|needs lock| CS[Critical section]
T2 -->|needs lock| CS
Learning question: What goes wrong if both threads update without coordination?
Caption: Shared memory is fast and race-prone.
Complete worked example: image upload service
Requirements
- Accept image uploads
- Virus scan
- Resize thumbnails
- Store metadata in a database
Option A — thread pool in one process
- Upload handler puts jobs on an in-memory queue
- Worker threads scan and resize
- Shared metrics counters in memory
Option B — multiple worker processes
- API process enqueues jobs in Redis/SQS
- N separate worker processes pull jobs
- Each can crash and restart independently
Practical hybrid
Many production systems:
- Stateless API processes (scale out)
- Async queue
- Worker processes or containers
- DB as source of truth
How it works in production
What you observe
| Signal | Hint |
|---|---|
| One container OOM killed | Process memory ceiling hit (all threads share it) |
| High load average, low progress | Too much contention or thrashing |
| Thread dump shows many BLOCKED | Lock contention |
| Worker process restarts often | Isolation working; fix root crash |
Containers and processes
A Docker container usually runs one main process (plus possible children). Scaling replicas scales processes/containers, not “one giant shared heap across machines.”
Security angle
Separate processes (or sandboxes) help when running untrusted plugins or risky native codecs — a thread is a weaker boundary.
Failure modes
1) Race conditions
- Trigger: unsynchronized shared writes.
- User: lost updates, rare corruption.
- Fix: locks, concurrent data structures, or avoid sharing.
2) Deadlocks
- Trigger: lock A then B vs lock B then A.
- User: requests hang until timeout.
- Fix: lock ordering; timeouts; simpler designs.
3) Overthreading
- Trigger: 10,000 OS threads for 10,000 connections.
- User: huge memory use; slow context switching.
- Fix: pools, async I/O, virtual threads where appropriate.
4) Assuming process death is fine without persistence
- Trigger: important state only in memory.
- User: lost work on restart.
- Fix: durable queues/databases.
5) One multithreaded monolith as SPOF
- Trigger: single process serves all traffic.
- User: total outage on crash.
- Fix: multiple processes/instances + load balancer.
Trade-offs
| Model | Strength | Weakness |
|---|---|---|
| Many processes | Isolation, simpler mental shared-state story | Higher overhead; IPC needed |
| Many threads | Cheap sharing; good for some shared caches | Races; whole-process fault domain |
| Async event loop | Excellent connection concurrency | CPU-bound work can stall the loop |
| Hybrid | Balance | Complexity |
Compare with related concepts
| Term | Relation |
|---|---|
| Process | Isolation boundary with own address space |
| Thread | Execution unit sharing process memory |
| Coroutine / virtual thread | Lighter concurrency constructs in runtimes |
| Goroutine (Go) | Runtime-scheduled lightweight threads (model differs from classic 1:1 OS threads) |
| Actor model | Prefer message passing over shared mutable state |
Common misunderstandings
- “More threads always means faster.”
- “Threads are completely isolated.”
- “Multi-process means no synchronization needed.”
- “Async code has no concurrency issues.”
- “The OS thread count equals the user request count in all modern servers.”
Check your understanding
Race condition on shared memory
Cross-process memory isolation failure
DNS failover
File system formatting error
Stronger isolation if one tab misbehaves
To double network bandwidth automatically
To avoid writing CSS
To use zero memory
Practice
You are designing a PDF rendering feature that uses a native C library known to crash occasionally.
- Prefer calling it in-thread in the main API process, or in a separate worker process? Why?
- How should the API learn that a worker died mid-job?
- Where should the PDF bytes live so any worker can access them?
- If two threads share a “rendered page cache” map, what hazard appears?
- Name one metric that would show lock contention.
Revision summary
- Process = running program with its own memory walls.
- Thread = worker inside a process sharing memory.
- Isolation vs cheap sharing is the core trade-off.
- Shared memory needs synchronization to avoid races.
- Crashes often kill the whole process (all its threads).
- Real servers mix processes, threads, and async models.
- Scale-out clones processes/containers across machines — not one infinite shared heap.
Glossary
| Term | Definition | Example |
|---|---|---|
| Process | OS program instance with address space | node server.js running |
| Thread | Execution path inside a process | Worker thread resizing images |
| Address space | Process memory map | Process A cannot see B’s heap |
| Race condition | Timing-dependent incorrect shared update | Lost counter increment |
| Mutex / lock | Exclusion for a critical section | Lock around balance update |
| Context switch | OS changes running thread/process | CPU switches workers |
| Concurrency | Many tasks in progress | 10,000 open connections |
| Parallelism | Simultaneous execution on hardware | 8 cores run 8 threads |
| IPC | Inter-process communication | Queue, socket, pipe |
| Thread pool | Reused set of threads | Fixed 32 workers |
Abbreviations and terminology
| Short | Full / note |
|---|---|
| OS | Operating System |
| CPU | Central Processing Unit |
| IPC | Inter-Process Communication |
| OOM | Out Of Memory (process killed for using too much RAM) |
| GIL | Global Interpreter Lock (CPython detail; advanced) |
| API | Application Programming Interface |
What to learn next
Primary next lesson: Latency vs Throughput vs Bandwidth
Then: concurrency vs parallelism and language-specific threading lessons when you specialize.
Track: Engineering Foundations
Previous: Latency vs Throughput vs Bandwidth
By Shubham Jain