The Core Difference: Memory
| Aspect | Process | Thread |
|---|---|---|
| Memory space | Own isolated address space | Shares memory with other threads in the same process |
| Communication | IPC — pipes, sockets, shared memory (slow) | Direct shared variables (fast) |
| Creation cost | Expensive — OS allocates new address space | Cheap — reuses parent process's memory |
| Crash isolation | One process crashing doesn't affect others | One thread crashing can corrupt the whole process |
| Context switch | Expensive — swaps memory page tables | Cheaper — same address space, just swaps registers/stack |
| Analogy | Separate houses | Roommates sharing one house |
A Process Owns Resources; Threads Use Them
When the operating system starts a program, it creates a process with its own:
- Virtual memory address space (code, heap, stack)
- File descriptors and open handles
- Environment variables and security context
A process can then spawn one or more threads, each with its own call stack and program counter (so each thread can be paused/resumed independently), but all threads share the same heap, file descriptors, and global variables.
Why Threads Are Risky: Shared Memory
Because threads share memory, two threads writing to the same variable at the same time causes a race condition unless protected by synchronization (locks, mutexes, semaphores). This is the trade-off: threads communicate fast because they share memory, but that same sharing is the source of the hardest bugs in concurrent programming — and the reason a single thread's crash can bring down the entire process.
Multithreading vs Multiprocessing
| Aspect | Multithreading | Multiprocessing |
|---|---|---|
| Best for | I/O-bound work (waiting on network/disk) | CPU-bound work (heavy computation) |
| Memory overhead | Low — shared memory | Higher — each process has its own memory |
| True parallelism | Depends on language (blocked by GIL in CPython) | Yes — each process can run on its own CPU core |
| Fault tolerance | Lower — shared state, shared crash domain | Higher — isolated crash domain |
Node.js: "Single-Threaded" but Not Quite
Node.js runs your JavaScript on a single main thread, driven by an event loop that handles asynchronous I/O without blocking. This is why a single slow synchronous computation can freeze an entire Node.js server — there's no second thread to pick up other requests while it runs.
Under the hood, Node uses a libuv thread pool for things like file system calls and DNS lookups, and exposes the worker_threads module for genuinely parallel CPU-bound work — but your application code itself executes on one thread unless you explicitly opt into workers.
⚠️ Python's GIL Limits Threading for CPU-Bound Work
CPython's Global Interpreter Lock (GIL) allows only one thread to execute Python bytecode at a time. Multithreading still helps for I/O-bound tasks (the GIL is released during I/O waits), but for CPU-bound parallelism, Python developers reach for the multiprocessing module instead, trading shared-memory speed for true multi-core execution.
Context Switching Cost
Switching the CPU from running one thread/process to another requires saving and restoring state. Process switches are more expensive because the CPU must also reload the memory page table — threads within the same process skip this since they already share the same address space. This is part of why thread pools and async I/O are often preferred over spawning many OS processes for highly concurrent servers.
💡 Rule of Thumb
Reach for threads when tasks need to share data frequently and you can manage synchronization carefully. Reach for processes when you need fault isolation, true CPU parallelism, or are running untrusted/independent workloads.
How We Research and Update This Guide
We test the underlying formula or workflow, compare outputs with reliable references, and revise examples whenever the page content changes.
- The workflow or formula is tested directly in the tool and compared against independent reference examples.
- Examples are kept practical so readers can verify the result without hidden assumptions.
- Pages are revised whenever the interface, calculation flow, or surrounding guidance materially changes.
Frequently Asked Questions — Thread vs Process
A process is an independent running program with its own isolated memory space — the operating system gives it its own address space, file handles, and resources. A thread is a unit of execution within a process — multiple threads in the same process share that process's memory and resources but each has its own call stack and program counter, allowing them to run concurrently while sharing data directly.
Threads within the same process share the same memory space, so they can read and write shared variables directly — communication is just memory access. Processes have separate memory spaces, so they must use inter-process communication (IPC) mechanisms like pipes, sockets, shared memory segments, or message queues, which involve the operating system and are significantly slower.
A context switch happens when the CPU stops executing one thread or process and starts executing another, saving the current state (registers, program counter, stack pointer) and loading the next one's state. Process context switches are more expensive than thread context switches because the CPU must also swap memory mappings (the page table) — switching between threads in the same process skips this step since they share the same address space.
Yes, typically. Because threads share the same memory space, an unhandled exception or memory corruption in one thread can corrupt shared state or crash the entire process, taking all its threads down with it. Processes are more isolated — if one process crashes, other independent processes are unaffected. This is a key trade-off: threads are faster to communicate but less fault-isolated than processes.
Node.js executes your JavaScript code on a single main thread, using an event loop to handle asynchronous I/O without blocking. However, Node.js itself uses a thread pool (libuv) under the hood for certain operations like file system access and DNS lookups, and you can spawn additional threads explicitly using the worker_threads module for CPU-intensive work.
The GIL is a mutex in CPython that allows only one thread to execute Python bytecode at a time, even on a multi-core machine. This means Python threads are good for I/O-bound work (waiting on network/disk, where the GIL is released) but do not give true parallel speedup for CPU-bound work — for that, Python developers use the multiprocessing module to get separate processes, each with its own GIL and own CPU core.