AJaiCodes logoAJaiCodes
HomeArticlesAbout
HomeArticlesThreads and Concurrency in Modern Java: A Technical Deep Dive

Threads and Concurrency in Modern Java: A Technical Deep Dive

Ajanthan Sivalingarajah
·Feb 27, 2026·7 min read
JavaThreadsConcurrencySynchronizationVirtual ThreadsFork/JoinCompletableFuture
10 views
ProgrammingConcurrencyBackend EngineeringSoftware ArchitectureProgramming Fundamentals
Modern Java Garbage Collectors (JDK 25+): A Deep DiveReactive Programming (RxJava) vs Virtual Threads (Java 21+)

AjaiCodes

A modern tech blog platform where developers share knowledge, insights, and experiences in software engineering and technology.

Quick Links

  • Home
  • Articles
  • About

Legal

  • Privacy Policy
  • Terms of Service

© 2026 AjaiCodes. All rights reserved.

Threads and Concurrency in Modern Java: A Technical Deep Dive#

Audience: Beginner to Architect
JDK Context: Java 8 → Java 21 (including Virtual Threads and Structured Concurrency)
Scope: Threads, memory model, synchronization, executors, Fork/Join, CompletableFuture, virtual threads, and architectural concurrency patterns.


Introduction#

Concurrency is one of the defining strengths of the Java platform. From early Thread APIs to modern constructs like CompletableFuture, ForkJoinPool, and Virtual Threads (Project Loom), Java has evolved into a powerful platform for building scalable, concurrent systems.

Understanding concurrency is not just about writing multi-threaded code. It is about:

  • Correctness under parallel execution
  • Memory visibility and ordering
  • Throughput under load
  • Latency predictability
  • Resource utilization efficiency

This article walks from foundational concepts to modern concurrency architecture in Java 21.


1. What Is a Thread?#

A thread is a lightweight unit of execution within a process.

Each Java application starts with at least one thread:

  • The main thread

Additional threads allow multiple tasks to execute concurrently.


1.1 OS Threads vs Java Threads#

Historically, Java threads are mapped 1:1 to OS threads.

Java Thread 1  →  OS Thread 1
Java Thread 2  →  OS Thread 2
Java Thread 3  →  OS Thread 3

This model is called platform threads.

Platform threads are:

  • Expensive to create
  • Limited in number (memory + kernel scheduling)
  • Suitable for CPU-bound workloads

2. Thread Lifecycle#

A thread in Java goes through several states.

stateDiagram-v2 [*] --> New New --> Runnable Runnable --> Running Running --> Blocked Blocked --> Runnable Running --> Terminated

States Explained#

StateMeaning
NEWThread object created but not started
RUNNABLEEligible to run
BLOCKEDWaiting for monitor lock
WAITINGWaiting indefinitely
TIMED_WAITINGWaiting with timeout
TERMINATEDFinished execution

3. The Java Memory Model (JMM)#

Concurrency correctness depends on memory visibility.

Modern CPUs reorder instructions for optimization. Without memory rules, threads may see stale values.

The Java Memory Model defines:

  • Happens-before relationships
  • Visibility guarantees
  • Ordering rules

3.1 Happens-Before Rule#

If A happens-before B, then B sees effects of A.

Examples:

  • Thread start establishes happens-before
  • Lock release happens-before lock acquisition
  • Write to volatile happens-before subsequent read

4. Race Conditions#

A race condition occurs when two threads access shared mutable state without synchronization.

Example:

class Counter {
    int count = 0;
    void increment() {
        count++;
    }
}

count++ is not atomic. It expands to:

  1. Read
  2. Increment
  3. Write

Two threads may interleave operations and lose updates.


5. Synchronization#

Java provides multiple synchronization mechanisms.


5.1 Synchronized Keyword#

synchronized (lock) {
    // critical section
}

What Happens Internally?#

Each object has a monitor.

Thread A → acquires monitor → executes → releases monitor
Thread B → waits

Properties#

  • Mutual exclusion
  • Visibility guarantees
  • Reentrant

5.2 ReentrantLock#

More flexible than synchronized.

Features:

  • Try-lock
  • Fairness policy
  • Interruptible locking

6. Volatile Keyword#

volatile ensures:

  • Visibility across threads
  • Prevents instruction reordering

It does NOT guarantee atomicity.

Thread A writes volatile variable
Thread B immediately sees updated value

Use volatile for:

  • Flags
  • State indicators
  • Single-writer scenarios

7. Atomic Variables#

Package: java.util.concurrent.atomic

Example:

AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();

Internally uses:

  • Compare-And-Swap (CAS)
  • CPU-level atomic instructions

8. Thread Pools and Executors#

Creating threads manually is inefficient.

Java provides the Executor framework.


8.1 Executor Architecture#

Task → Executor → Thread Pool → Worker Threads

8.2 Fixed Thread Pool#

ExecutorService executor = Executors.newFixedThreadPool(4);

Best for:

  • CPU-bound tasks
  • Controlled parallelism

8.3 Cached Thread Pool#

Executors.newCachedThreadPool();

Best for:

  • Short-lived async tasks
  • I/O-heavy workloads

9. Fork/Join Framework#

Designed for parallelizable divide-and-conquer tasks.

Used internally by:

  • Parallel Streams
  • CompletableFuture (default pool)

9.1 Work Stealing#

Each worker thread has its own deque.

Worker 1: [Task A, Task B]
Worker 2: [Task C]
Worker 3: []

Worker 3 steals from Worker 1

This improves load balancing.


9.2 Fork/Join Model#

graph TD A[Task] --> B[Subtask 1] A --> C[Subtask 2] B --> D[Combine] C --> D

10. CompletableFuture#

Modern asynchronous programming model.

Supports:

  • Non-blocking execution
  • Functional composition
  • Async pipelines

Example:

CompletableFuture
    .supplyAsync(() -> fetch())
    .thenApply(data -> transform(data))
    .thenAccept(result -> save(result));

Enables structured asynchronous flow without explicit thread management.


11. Virtual Threads (Project Loom)#

Introduced as stable in Java 21.

Virtual threads are lightweight threads managed by the JVM.


11.1 Platform vs Virtual Threads#

Platform Thread → OS Thread
Virtual Thread → JVM Managed → Mounted on Platform Thread

11.2 Architecture#

graph TD A[Virtual Thread 1] --> P1[Platform Thread] B[Virtual Thread 2] --> P1 C[Virtual Thread 3] --> P2

Thousands of virtual threads can run on a few platform threads.


11.3 Why Virtual Threads Matter#

Traditional blocking I/O consumes platform threads.

With virtual threads:

  • Blocking becomes cheap.
  • Thread-per-request model becomes viable again.
  • Simplifies reactive complexity.

Example:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> handleRequest());
}

12. Structured Concurrency#

Structured Concurrency provides:

  • Scoped lifetimes for concurrent tasks
  • Better cancellation handling
  • Improved error propagation

Conceptually:

Parent Task
 ├── Child Task A
 ├── Child Task B
 └── Child Task C

All children must complete before parent finishes.


13. Concurrency Models in Architecture#

ModelBest For
Thread-per-requestVirtual threads
Event loopReactive systems
Fork/JoinCPU-bound tasks
CompletableFutureAsync pipelines
Actor modelDistributed systems

14. Common Concurrency Problems#

  • Deadlocks
  • Livelocks
  • Starvation
  • Thread leakage
  • Excessive context switching

14.1 Deadlock Example#

graph LR A[Thread 1] -->|holds Lock A| B[Lock A] B -->|waits for| C[Lock B] D[Thread 2] -->|holds Lock B| C C -->|waits for| B

Avoid by:

  • Consistent lock ordering
  • Timeouts
  • Lock-free structures

15. Performance Considerations#

Key metrics:

  • Context switching cost
  • Lock contention
  • False sharing
  • CPU cache coherency
  • Thread scheduling overhead

For CPU-bound tasks:

  • Fixed-size thread pools

For I/O-bound workloads:

  • Virtual threads

16. From Beginner to Architect Perspective#

Beginner Focus#

  • Thread lifecycle
  • Synchronized and volatile
  • Executors

Intermediate Focus#

  • JMM and happens-before
  • Atomic classes
  • Fork/Join

Architect Focus#

  • Virtual threads vs reactive
  • Structured concurrency
  • Throughput vs latency tradeoffs
  • Capacity planning

Conclusion#

Modern Java concurrency has evolved from low-level thread manipulation to highly expressive, scalable concurrency models.

With Java 21:

  • Virtual threads simplify blocking I/O
  • Structured concurrency improves safety
  • Executors and Fork/Join enable scalable CPU parallelism

Understanding concurrency today means understanding both:

  • Correctness (memory model, visibility)
  • Performance (thread scheduling, CPU utilization, scalability)

Concurrency is not simply about running tasks in parallel. It is about designing systems that behave predictably under load while maintaining clarity, maintainability, and correctness.