Welcome back to The Coder Cafe; I hope everything is OK for you. I’m still trying to figure out the best moment to send the emails; this is the reason why emails were sent later than last week. As we have subscribers from 59 countries all over the world, it’s impossible to find a time that would be the best for everyone.
Anyway, this week, we will explore the world of concurrency. Let’s start today with this question: what are the differences between concurrency and parallelism?
It’s fairly common for developers to mix up the concepts of concurrency and parallelism, but they are actually quite distinct. Let’s approach these two concepts using a coffee shop analogy.
Imagine a coffee shop with one waiter who takes orders and makes coffee using a single machine. Customers wait in line, place their orders, and then wait for their coffee:
If the waiter struggles to keep up with the number of customers, the coffee shop owner may decide to speed up the overall process by engaging a second waiter and buying a second coffee machine. Now, two waiters can serve customers at the same time:
In this setup, the two waiters work independently. Each has its own coffee machine, allowing the shop to serve customers twice as fast. This is parallelism—executing multiple tasks simultaneously by adding more resources (waiters and machines).
If we want to scale further, we can keep adding more waiters and machines, but this approach has its limits. For example, buying multiple coffee machines can become overly expensive.
Instead of just adding more machines, we could rethink how the work is structured. One way to do this is by splitting the tasks between the waiters: one takes orders, and the other makes coffee using a coffee machine. To avoid blocking the customer queue, we introduce a second queue where customers wait for their coffee (similar to Starbucks):
Here, what we have changed is the overall structure; this is concurrency. The tasks are structured to be handled independently, even if they don’t happen at the same time.
Now, imagine each thread in an application represents a role: one thread for taking orders and another for making coffee. These threads work independently but must coordinate. For example, the ordering threads need to pass information about what to prepare to the coffee-making thread.
What if we want to increase the shop’s capacity further? Since making coffee takes longer than taking orders, we could hire another waiter specifically to make coffee:
Has the structure been changed? No. It’s still a three-step design:
Accept orders
Make coffee
Serve coffee
However, by adding another waiter just for coffee-making, we introduce parallelism into this specific step without altering the overall concurrency structure.
Remember that while parallelism and concurrency are distinct, they can complement each other. Parallelism can exist without concurrency, but concurrency can enable parallelism as it provides a structure to solve a problem with parts that may be parallelized.
To summarize:
Concurrency is about structure, and we design a solution so that parts can be managed independently. This doesn’t necessarily mean they run at the same time but rather that they are organized in a way that allows for more efficient execution.
Parallelism is about execution, performing multiple tasks simultaneously by adding more threads.
Tomorrow, we will keep exploring the concepts of concurrency and parallelism by discussing coroutines.
Hi! Some feedback on formatting: in the Substack App on iOS with dark theme images are not visible (main color of the image is black with transparent background).
I love the use of analogies to explain concepts, keep em coming :)