Java Concurrency and Multithreading, Part 1: What Concurrency Has to Do with Multithreading

What's the relationship between high concurrency and multithreading? I never create threads in my code, so why does everyone start talking about multithreading the moment the subject comes up? My opening post digs into why we should learn multithreading, and how high concurrency turns into multithreading.

Series index: Java Intermediate/Advanced Programming: article index

Disclaimer up front: everything in this series is my own understanding of the subject, typed out by hand. It may well contain mistaken views or misunderstandings. Use it as reference only, and corrections are welcome.

A lot of tutorials open with computer architecture — how CPUs work, what processes and threads are — and then dive straight into academic content. As a beginner you inevitably wonder: I’m not using threads anywhere in my code, so why does every conversation about high concurrency turn into a conversation about multithreading?

Interest is the best teacher, and I was curious too: what’s the connection? I never spawn threads, so why is multithreading always part of the story? That’s why this opening post digs into why we should learn multithreading, and how high concurrency turns into multithreading.

What Happens During a Request

DNS resolution, the HTTP handshake, listening ports — let’s skip all that, it isn’t the point. Assume the request has already been routed to our program and it starts handling it.

When a request arrives, the framework maps it to an HttpRequest object — everything is an object — then grabs a thread from the thread pool and hands the request to it. Thread pools exist to save resources; even without one, a thread would be created to run it, since creating and destroying threads costs real resources. Requests can pile up, and handling them one at a time with everyone else queued is wasteful — we have multi-core, multi-threaded CPUs that can handle many things at once, so let requests be processed concurrently, each on its own thread.

With few threads everything stays civil: finish one, start the next, read data, process it, write it back, no problems. But what happens when there are many threads?

Concurrency and Multithreading

As established, in Java everything is an object: each incoming request is an object handed to a thread. If those requests all run the same code — the classic flash sale where everyone grabs the same product — then all those threads run identical logic against the same product row, decrementing the same stock value. At that point high concurrency becomes multithreading: every thread wants to modify one shared resource, and that’s where trouble starts. Here’s a diagram to make it clearer — a simplified illustration, nothing more:

High concurrency illustration

That’s why so many companies ask for multithreading experience. Once concurrency ramps up, you get many threads mutating one resource, and if they aren’t coordinated, that resource breaks.

What Goes Wrong with Multiple Threads

Threads look like they run simultaneously, but at the bottom they don’t: CPU cores are finite and can handle one task at a time. The CPU is just extremely fast, so it slices time and rotates between threads, creating the illusion of parallelism. It’s really just the CPU switching quickly — and that causes a problem: a thread never knows where it will be kicked off the CPU, and when it comes back it resumes from exactly where it was evicted.

Suppose:

  1. Thread A gets scheduled, reads stock = 1, then gets evicted and suspended.
  2. Thread B gets scheduled, also reads stock = 1, then gets evicted and suspended.
  3. Thread A resumes, decrements stock — now the database has 0 — then gets evicted again.
  4. Thread B resumes and decrements stock as well — now it’s -1.

You end up with several people winning the same order, stock going negative, and more orders succeeding than there are items. That’s a textbook multithreading bug — and exactly why learning concurrent programming matters.