Skip to main content

Command Palette

Search for a command to run...

Concurrency vs. Parallelism: A Coffee Shop Guide for Developers

Updated
4 min read

If you ask ten developers to explain the difference between concurrency and parallelism, you might get ten slightly different answers. It’s one of those fundamental concepts that is easy to grasp abstractly but tricky to visualize in practice.

To understand where we are today, we have to look at where we started.

The Single-Core Era vs. The Multi-Core Revolution

Back in the “old days” of computing, we relied on single-core processors. Despite this limitation, computers still seemed to multitask. You could listen to music while typing a document, and it felt simultaneous. But it was rather an illusion.

The processor was frantically switching between tasks—giving a few milliseconds to the music player, then a few milliseconds to the word processor—so quickly that we humans couldn’t notice the gap. This is the foundation of threading.

Today, we have multi-core processors (dual-core, quad-core, octa-core, etc.) that can physically execute multiple instructions at the exact same instant. However, to utilize that power, we first need to design our software correctly.

Defining the Terms

  • Concurrency is about structure. It is the composition of a program into small, independent tasks that can be executed out of order or in partial order.

  • Parallelism is about execution. It is the simultaneous execution of distinct tasks.

You can have concurrency without parallelism (the single-core example), but you generally cannot have parallelism without concurrency.

The “Context Switch”

In a concurrent system, the CPU has to save the state of the current thread (variables, instruction pointers) and load the state of the next thread. This is called a Context Switch.

Context switching is necessary for responsiveness, but it isn’t free. If you have a single processor and you spin up 1,000 threads, your computer might spend more time switching between them than actually doing the work!

The Coffee Shop Analogy

Let’s visualize this with a simple office breakroom scenario.

Scenario A: Concurrent but NOT Parallel
Imagine an office breakroom with two lines of developers but only one coffee machine.

  • The developers are independent “tasks”.

  • The queues represent the structure (Concurrency).

  • The coffee machine is the CPU.

Even though there are two lines, the coffee machine can only brew one cup at a time. It might serve the first person in Line A, then switch to the first person in Line B. This is concurrency. The tasks are progressing, and no single line is completely blocked, but they are sharing the same resource.

Scenario B: Concurrent AND Parallel
Now, management buys a second coffee machine.

  • We still have the two lines (Concurrency).

  • But now, the person in Line A and the person in Line B can press “Brew” at the same instant.

This is parallelism. Because we structured the problem correctly (separate queues), adding more hardware (the second coffee machine) instantly doubled our throughput. Let’s understand with a diagram.

An example code in Java should clarify further.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CoffeeShop {
    public static void main(String[] args) {
        // Scenario 2: Parallelism (2 Coffee Machines -> 2 Threads)
        ExecutorService coffeeMachines = Executors.newFixedThreadPool(2);

        Runnable makeCoffee = () -> {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + " is brewing coffee...");
            try { 
                Thread.sleep(2000); 
            } catch (InterruptedException e) {} // Simulate brewing
            System.out.println(threadName + " is finished!");
        };

        // Two people order at the same time
        coffeeMachines.submit(makeCoffee);
        coffeeMachines.submit(makeCoffee);

        coffeeMachines.shutdown();
    }
}

If you run this, both “brewing” messages appear instantly. If you change the thread pool to 1 (concurrency), the second message would only appear after the first one finishes.

Summary

Designing for concurrency means structuring your program so that tasks don’t rely on each other unnecessarily. If you write a program where every step must happen sequentially (Line A must finish before Line B starts), you can never parallelize it, no matter how many cores your system has.

Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.