What you'll learn
Quick Answer
Processes have separate memory; threads within a process share it. Sharing makes threads cheap to create and fast to communicate, and it is also why threads can corrupt shared data and processes cannot.
The difference that explains all the others
A process is a running program with its own memory space. Two processes cannot read each other's variables — the operating system prevents it. That isolation is why a crashing browser tab does not take down the whole browser.
A thread is a unit of execution inside a process. Threads in the same process share the same memory. They have their own stack and program counter, but the heap is common.
Every consequence follows from that:
- Creating a thread is much cheaper, because there is no new memory space to set up.
- Threads communicate by reading the same variables; processes need explicit inter-process communication.
- One thread crashing can bring down the whole process; one process crashing leaves others untouched.
- Threads can corrupt shared data. Processes cannot, because they have nothing shared to corrupt.
The race condition, demonstrated
Four threads each increment a shared counter 50,000 times. The expected total is 200,000. Running it in Java:
static int counter = 0;
// four threads, each doing:
for (int k = 0; k < 50000; k++) counter++;
expected 200000, unsynchronised got: 76235
Over 120,000 increments vanished. The reason is that counter++ is not one operation — it is read, add one, write back. Two threads can read the same value, both add one, and both write back the same result. One increment is lost, and it happens constantly.
That number is also different on every run, which is what makes these bugs so unpleasant: the program is correct most of the time and wrong unpredictably.
With an atomic counter, which makes the read-modify-write indivisible:
with AtomicInteger got: 200000
Exactly right, every time. That is what synchronisation buys.
When to use which
Threads suit work inside one application that needs shared data: a web server handling many requests against a shared cache, a UI keeping its interface responsive while work happens in the background, or several downloads writing into one structure.
Processes suit isolation and true parallelism: running untrusted code, keeping one crash from taking down everything, and — in Python specifically — using multiple CPU cores.
That Python caveat is worth knowing because it surprises people. CPython's global interpreter lock means only one thread executes Python bytecode at a time, so threads do not give you parallel CPU work. They still help for input/output waiting — network calls, file reads — because the lock is released while waiting. For CPU-bound work use multiprocessing, which uses separate processes and sidesteps the lock.
The related terms you will be asked about
- Context switch — the operating system saving one task's state and loading another's. Switching between threads is cheaper than between processes, because the memory mappings do not change.
- Concurrency vs parallelism — concurrency is dealing with several things at once, which is possible on a single core by interleaving. Parallelism is doing them at the same instant, which needs multiple cores.
- Critical section — the part of the code that touches shared data. It must be entered by only one thread at a time.
- Mutex — a lock ensuring exactly that.
- Deadlock — two threads each holding a lock the other needs, so neither proceeds. See deadlock explained.
How this is actually asked
Rarely as a definition. More often as: "Why did this counter give the wrong number?", "How would you make this thread-safe?", or "Why is your Python program not faster with four threads?"
Good answers name the shared mutable state, identify the critical section, and propose the smallest correct fix — an atomic type or a lock around just the shared access. Locking too much is a real cost: a lock around the entire loop serialises the threads and removes the point of using them.
The underlying principle worth stating: shared mutable state is the source of concurrency bugs. Remove the sharing, or remove the mutation, and most of the problem disappears. That is why immutable data and message passing are popular in concurrent systems.
