AlgoMaster Logo

Introduction to Concurrency

Last Updated: February 5, 2026

Ashish

Ashish Pratap Singh

Every modern software system deals with concurrency. Your phone runs dozens of apps simultaneously. A web server handles thousands of requests at once.

Understanding concurrency is fundamental to building software that works in the real world. Yet concurrency remains one of the most misunderstood topics in programming.

This chapter builds the foundation you need to think clearly about concurrent systems.

What is Concurrency?

Concurrency is the ability of a system to handle multiple tasks during overlapping time periods. Notice that it's "overlapping time periods," and not "at the same time." This distinction matters.

In software terms, concurrency means structuring a program so that multiple tasks can make progress. The tasks might not execute simultaneously, but the program is organized to handle them in an interleaved fashion.

Concurrency is about dealing with multiple things at once.

Benefits of Concurrency

Before diving into how concurrency works, let's understand why we need it. There are three core benefits.

1. Responsiveness

Without concurrency, a program does one thing at a time. If that thing takes 10 seconds (e.g., downloading a file, processing data), the user waits 10 seconds with no feedback, unable to do anything else.

With concurrency, the program can handle the long-running task while remaining responsive to user input. The download happens in the background and the application stays interactive.

This is why every modern GUI framework uses concurrency. The main thread handles UI events. Background threads handle slow operations.

2. Resource Utilization

Consider a web server. Most of the time spent handling a request is waiting: waiting for the database to respond, waiting for an external API, waiting for the disk. If the server handled one request at a time, the CPU would sit idle during all that waiting.

With concurrency, the server handles multiple requests. While one request waits for the database, the CPU processes another request. The system resources (CPU, memory, network) stay busy instead of sitting idle.

Here is a rough calculation: if a request spends 90% of its time waiting for I/O, a single-threaded server uses only 10% of the CPU capacity. With 10 concurrent requests, the server can approach full CPU utilization.

3. Throughput

Throughput is the amount of work completed in a given time period. Concurrency increases throughput in two ways:

I/O-bound workloads

By overlapping waiting times, more operations complete per second. A single-threaded server might handle 100 requests/second. A concurrent server handling 10 requests simultaneously might achieve 800 requests/second (not quite 10x due to overhead, but close).

For CPU-bound workloads (with parallelism)

By using multiple CPU cores, computation happens faster. Sorting a billion numbers on one core might take 10 minutes. Splitting the work across 8 cores might take under 2 minutes.

The important insight: concurrency helps I/O-bound work even on a single core. Parallelism helps CPU-bound work by using multiple cores.

Challenges of Concurrency

If concurrency were easy, everyone would do it perfectly. But concurrent programming is hard. Here is why.

1. Non-Determinism

A sequential program runs the same way every time. Given the same input, you get the same output. You can reason about it step by step.

A concurrent program has multiple possible execution orders. The operating system's scheduler decides which thread runs when. Different runs can produce different results, even with identical inputs.

This non-determinism makes concurrent programs hard to reason about. The bug might appear once in a thousand runs, only in production, only under load.

2. Race Conditions

A race condition occurs when the program's correctness depends on the timing of events. Two threads race to access shared data, and whoever gets there first determines the outcome.

Classic example: two threads incrementing a counter.

Both threads read the same value, both calculate the same result, and one write overwrites the other. This is called a lost update.

Race conditions are insidious because they don't always manifest. The code might work correctly thousands of times, then fail once under slightly different timing conditions.

3. Deadlocks

A deadlock occurs when two or more threads are waiting for each other, and none can proceed.

Thread A holds Lock 1 and wants Lock 2. Thread B holds Lock 2 and wants Lock 1. Neither can proceed. The program hangs forever.

Deadlocks don't corrupt data like race conditions. Instead, the program simply stops making progress. In production, this often manifests as a service becoming unresponsive under load.

4. Debugging Difficulty

Concurrent bugs are notoriously hard to find because:

  • They are intermittent: The bug only appears under specific timing conditions.
  • Observation changes behavior: Adding print statements or attaching a debugger changes timing, potentially making the bug disappear.

This is called a "heisenbug," a bug that seems to disappear when you try to observe it (named after Heisenberg's uncertainty principle).

5. Complexity

Even without bugs, concurrent code is harder to understand than sequential code. You need to think about:

  • What data is shared between threads?
  • What synchronization protects that data?
  • What happens if thread A runs before thread B? What about the reverse?
  • Can these operations be reordered by the compiler or CPU?

This mental overhead increases the cognitive load on developers and reviewers.

Where Concurrency Appears in Real Systems

Concurrency is tricky to get right, but modern software would not work without it. You’ll see it everywhere, from the OS that runs your code to the distributed systems your code talks to.

Operating Systems

Every process and thread relies on OS-level concurrency. The scheduler decides which thread gets CPU time next, while multiple processes share memory and I/O devices. File systems also deal with concurrency constantly, handling overlapping reads and writes.

Web Servers

When you open a website, the server processes your request alongside thousands of others. A single request often involves multiple slow operations, such as:

  • querying databases
  • calling external APIs
  • reading from disk
  • computing results

To stay fast and maximize throughput, the server overlaps these operations instead of doing them strictly one after another.

Databases

Databases are concurrent by design:

  • many queries run at the same time
  • transactions must stay isolated from each other
  • writers must not corrupt data that readers are using
  • replication copies data across servers concurrently

That’s why concepts like locks, MVCC, and isolation levels matter so much if you want reliable applications.

User Interfaces

Every GUI app depends on concurrency:

  • the main thread handles user input (clicks, keystrokes)
  • background work handles slow tasks (network, disk, heavy computation)
  • rendering may run on additional threads or a separate pipeline

Without this separation, the UI would freeze the moment something slow happens.

Distributed Systems

Modern backends run across many machines, and each machine runs multiple threads. A single request can pass through load balancers, app servers, caches, databases, and message queues.

Every hop involves concurrent work, and every network call can fail, timeout, or arrive out of order. Concurrency becomes harder when it crosses machine boundaries.

Quiz

Introduction to Concurrency Quiz

1 / 10
Multiple Choice

What is the main distinction between concurrency and parallelism?