DevNotes
Support
Multithreading · Chapter 01

Introduction to Multithreading

Processes vs threads, concurrency vs parallelism, why backends use threads, what threads share, and the costs and risks that the rest of this topic teaches you to control.

◷ 7 min read§ 13 sections? 6 interview questions{ } 1 code examples

#Introduction

Every Java backend is multithreaded, whether you write a single new Thread() or not. Tomcat serves each HTTP request on a worker thread, HikariCP hands out connections to many threads at once, @Async and @Scheduled run on thread pools, and the JVM itself runs garbage-collector and JIT-compiler threads alongside your code.

This topic goes from the basics — what a thread is and how to start one — through synchronization, the Java Memory Model, locks, atomics, executors, CompletableFuture, concurrent collections and virtual threads, to Spring's @Async and classic coding scenarios.

#Program, Process and Thread

TermMeaning
ProgramCode on disk — a .jar file is just bytes until it runs
ProcessA running program with its own memory space, file handles and resources. Each java -jar app.jar starts one JVM process
ThreadA path of execution inside a process. A process has one or more threads that share its memory
  • JVM process
    • Heapshared by all threads — objects, static fields
    • Metaspaceshared — class metadata
    • main threadown stack, own program counter
    • http-nio-8080-exec-1own stack, own program counter
    • http-nio-8080-exec-2own stack, own program counter
    • GC and JIT threadsJVM housekeeping

What Threads Share — and What They Don't

Shared by all threadsPrivate to each thread
Heap — every object created with newStack — local variables and method-call frames
Static fieldsProgram counter — the next instruction to run
Class metadata (Metaspace)ThreadLocal values
Open files, sockets, connection pools

#Process vs Thread

ProcessThread
Own memory spaceShares the process's memory
Heavyweight to create (OS allocates address space)Lighter to create (but still an OS resource)
Communicates via IPC — sockets, pipes, filesCommunicates by reading and writing shared objects
A crash usually doesn't affect other processesAn uncaught error affects only that thread, but corrupted shared state affects all
Context switch is expensiveContext switch is cheaper

#Why Use Multiple Threads?

  • Responsiveness — a server can keep accepting requests while others are being processed.
  • Throughput — while one thread waits for the database or a remote API, others use the CPU.
  • Using multiple cores — CPU-heavy work (image processing, report generation) can be split across cores.
  • Background work — sending emails, cleaning caches and polling queues without blocking users.

#Concurrency vs Parallelism

A favourite interview question.

ConcurrencyParallelism
Dealing with many tasks at onceDoing many tasks at the same instant
Tasks make progress in overlapping time periodsTasks literally run simultaneously
Possible on a single core (time slicing)Needs multiple cores
About structure — managing independent tasksAbout execution — speeding up work
Thread A runs
Thread B runs
Thread A continues
Single core — concurrency by time slicing
Core 1
Thread A
Core 2
Thread B
Core 3
Thread C
Core 4
Thread D
Four cores — parallelism

A web server handling 200 requests on 8 cores is doing both: the requests are concurrent, and up to 8 of them execute in parallel at any instant.

#I/O-Bound vs CPU-Bound Work

How many threads help depends on what the threads do.

I/O-boundCPU-bound
Mostly waiting — database queries, HTTP calls, file readsMostly computing — hashing, compression, calculations
Many more threads than cores help, because waiting threads don't use the CPUMore threads than cores don't help — they just compete and context-switch
Typical of REST APIs and microservicesTypical of batch processing and analytics
Virtual threads (Java 21) shine hereUse roughly one thread per core (ForkJoinPool, parallel streams)

#The Main Thread

Every Java program starts with one non-daemon thread called main:

Java
public class Main {
    public static void main(String[] args) {
        Thread current = Thread.currentThread();
        System.out.println(current.getName());   // main
        System.out.println(current.getId());     // usually 1
        System.out.println(Runtime.getRuntime().availableProcessors() + " cores");
    }
}
Output
main
1
8 cores

The JVM exits when all non-daemon threads have finished — not when main returns. (getId() is deprecated since Java 19 in favour of threadId().)

#The Costs and Risks of Threads

Threads aren't free:

CostWhy
MemoryEach platform thread reserves a stack — typically 512 KB–1 MB (-Xss)
Creation timeCreating an OS thread is a system call
Context switchingThe CPU saves and restores registers and loses cache warmth on every switch
Scheduling limitsThousands of platform threads overwhelm the OS scheduler

And they introduce whole new categories of bugs:

ProblemWhat goes wrongCovered in
Race conditionTwo threads update shared data and one update is lostRace conditions & synchronized
VisibilityOne thread's write is never seen by anotherJava Memory Model & volatile
DeadlockThreads wait for each other foreverDeadlock, livelock & starvation
StarvationA thread never gets the CPU or lock it needsDeadlock, livelock & starvation
Thread leaksPools that are never shut down; ThreadLocal values that are never cleanedExecutors; ThreadLocal

#How Java Supports Concurrency

Thread and Runnable — Java 1.0
synchronized, wait / notify — Java 1.0
java.util.concurrent: executors, locks, atomics, concurrent collections — Java 5
Fork/Join framework — Java 7
CompletableFuture, parallel streams — Java 8
Virtual threads — Java 21

Modern code rarely creates raw threads. It submits tasks to executors, coordinates with CompletableFuture and concurrency utilities, and — for high-concurrency I/O — uses virtual threads.

#Interview Questions

Q1What is the difference between a process and a thread?

A process is a running program with its own memory space; a thread is a unit of execution inside a process. Threads of the same process share the heap and static data but each has its own stack and program counter.

Q2What is the difference between concurrency and parallelism?

Concurrency is about managing multiple tasks whose execution overlaps in time — possible even on one core through time slicing. Parallelism is running tasks at literally the same instant on multiple cores.

Q3Which memory areas are shared between threads?

The heap (all objects), static fields and class metadata are shared. Each thread's stack — local variables and call frames — and its program counter are private.

Q4Are local variables thread-safe?

Yes — each thread gets its own copy on its own stack. But if a local variable references a shared object on the heap, the object itself is not protected.

Q5When does the JVM exit?

When all non-daemon threads have finished (or System.exit() is called). Daemon threads don't keep the JVM alive.

Q6Why not just create more threads to go faster?

For CPU-bound work, more threads than cores only add context switching. Each platform thread also costs memory and scheduling overhead. The right number depends on whether the work is CPU-bound or I/O-bound.

#Common Mistakes

  • Thinking multithreading always makes code faster — for CPU-bound work beyond the core count, and for very small tasks, it makes things slower.
  • Assuming a single-threaded test proves thread safety — concurrency bugs appear only under real interleavings.
  • Forgetting your code is already multithreaded — a Spring singleton bean with a mutable field is shared by every request thread.
  • Confusing concurrency with parallelism in interviews.

#Best Practices

  • Prefer immutable objects and local variables; share as little mutable state as possible.
  • Use executors and thread pools instead of creating threads manually.
  • Choose thread counts based on whether work is CPU-bound or I/O-bound.
  • Treat every Spring singleton (controllers, services, repositories) as shared by many threads — keep it stateless.
  • Use high-level utilities from java.util.concurrent before low-level wait/notify.

#Key Takeaways

  • A process has its own memory; threads inside it share the heap and static data but have their own stacks.
  • Concurrency is about structure (overlapping tasks); parallelism is about simultaneous execution on multiple cores.
  • I/O-bound work benefits from many threads; CPU-bound work from about one thread per core.
  • The JVM exits when all non-daemon threads finish.
  • Threads bring race conditions, visibility problems, deadlocks and starvation — the rest of this topic is about preventing them.