Introduction to Concurrency
โ Free lessonConcurrency is a program's ability to deal with many tasks over overlapping time periods โ making progress on several things without necessarily doing them at the same physical instant. It's the foundation of fast, stable, and scalable software, and it's how modern programs squeeze real performance out of multi-core processors. This lesson lays the groundwork for everything that follows.
Concurrency vs. Parallelism
Concurrency is about managing many tasks at once โ one worker rapidly switching between them. Parallelism is about executing many tasks at the same physical instant โ on separate CPU cores. Concurrency is a way to structure work; parallelism is a way to run it. You can have one without the other.
Concurrency โ 1 core
Parallelism โ 2 cores
Processes vs. Threads
Concurrency runs inside two kinds of containers. A process is an independent program with its own private memory space. A thread is a smaller unit of execution that lives inside a process and shares memory with its sibling threads. That shared memory is what makes threads fast to coordinate โ and also what makes them dangerous, as you'll see.
| Aspect | Process | Thread |
|---|---|---|
| Memory | Isolated, private | Shared with siblings |
| Creation cost | Heavy | Light |
| Communication | Slower (IPC) | Fast (shared memory) |
| A crash affects | Only itself | The whole process |
Race Conditions & Critical Sections
Because threads share memory, two of them can try to modify the same data at the same time โ and the final result depends on who wins the race. That's a race condition. The stretch of code that touches shared data and must not be entered by two threads at once is called the critical section. Protecting it is what most of this course is about.
โ ๏ธ The classic bug
Two threads each read a counter as 5, both add one, both write back 6. Two increments happened, but the counter only went up by one. No error, no crash โ just silently wrong data. This is why we need synchronization.
Concurrency Models
Languages tackle concurrency in different ways, but most fall into two families. Shared memory: threads read and write the same data and coordinate with locks (Java, C++). Message passing: independent workers own their data and communicate by sending messages, so there's nothing to lock (Go's channels, the Actor model). Neither is "better" โ they're different trade-offs you'll learn to choose between.
The Thread Lifecycle
Every thread moves through a fixed set of states from birth to death. Understanding this state machine is essential โ most concurrency bugs are really a thread stuck in the wrong state. Watch it cycle:
start(), bounces between running and the waiting states as it competes for the CPU or a lock, and reaches TERMINATED once โ with no way back.Same idea, four languages
Here's a taste of starting one thread โ the seed of all concurrency. Switch tabs to compare how each language expresses it:
๐งฉ Try it yourself
Run any snippet a few times. Do main and worker always print in the same order? They won't โ the scheduler decides, and it makes no promises. That unpredictability is the exact thing synchronization exists to tame.