AJaiCodes logoAJaiCodes
HomeArticlesAbout
HomeArticlesJava Concurrency Thread Types Explained with Code (JDK 25 LTS Deep Dive)

Java Concurrency Thread Types Explained with Code (JDK 25 LTS Deep Dive)

Ajanthan Sivalingarajah
·Mar 02, 2026·7 min read
Java ConcurrencyJava ThreadsJDK 25Virtual ThreadsProject LoomStructured Concurrency
6 views
JavaConcurrencyProgramming
Reactive Programming (RxJava) vs Virtual Threads (Java 21+)SOLID Principles in Modern Software Engineering

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.

Java Concurrency Thread Types Explained with Code (JDK 25 LTS Deep Dive)#

Target Audience: Beginner → Architect
JDK Context: JDK 25 LTS (latest LTS generation model, including Virtual Threads & Structured Concurrency)
Focus: Every major Java thread/concurrency type with practical code examples, deep explanations, and architectural alternatives.


1. Introduction#

Modern Java concurrency is no longer limited to Thread and synchronized. As of JDK 25 LTS, Java provides multiple concurrency models:

  • Platform Threads
  • Virtual Threads
  • Thread Pools (Executors)
  • Scheduled Executors
  • Fork/Join Framework
  • CompletableFuture
  • Parallel Streams
  • Structured Concurrency (Scoped tasks)
  • Reactive-style async execution

Each model serves a different architectural purpose.

This guide explains:

  • When to use each
  • Internal mechanics
  • Code samples
  • Performance trade-offs
  • Alternative approaches

2. Platform Threads (Classic OS-backed Threads)#

2.1 What Are Platform Threads?#

Platform threads map 1:1 to OS threads.

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

They are expensive compared to virtual threads.


2.2 Example: Creating Platform Threads#

public class PlatformThreadExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("Running in: " + Thread.currentThread());
        });

        thread.start();
    }
}

2.3 Internals#

  • Allocates native stack memory
  • Scheduled by OS
  • Context switching handled by kernel
  • Blocking I/O blocks OS thread

2.4 When to Use#

  • CPU-bound workloads
  • Limited number of threads
  • Legacy systems

2.5 Alternative#

Use ExecutorService or Virtual Threads for scalable applications.


3. Executor Framework (Thread Pools)#

3.1 Why Executors?#

Creating threads manually does not scale.

Task → Queue → Thread Pool → Worker Thread

3.2 Fixed Thread Pool Example#

import java.util.concurrent.*;

public class FixedPoolExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(4);

        for (int i = 0; i < 10; i++) {
            int taskId = i;
            executor.submit(() ->
                System.out.println("Task " + taskId + " handled by " +
                        Thread.currentThread().getName())
            );
        }

        executor.shutdown();
    }
}

3.3 Architecture (ASCII)#

+-------------------+
|     Task Queue    |
+-------------------+
        ↓
+-------------------+
|  Thread Pool (4)  |
| T1  T2  T3  T4    |
+-------------------+

3.4 Alternative Approaches#

ScenarioBetter Option
Blocking I/OVirtual Threads
CPU-intensiveForkJoinPool
Async PipelinesCompletableFuture

4. Scheduled Executor#

Used for periodic or delayed tasks.

ScheduledExecutorService scheduler =
    Executors.newScheduledThreadPool(1);

scheduler.scheduleAtFixedRate(() -> {
    System.out.println("Running every 2 seconds");
}, 0, 2, TimeUnit.SECONDS);

Use case:

  • Polling systems
  • Health checks
  • Background cleanup

Alternative: java.time + structured scheduler frameworks.


5. Fork/Join Framework#

Designed for divide-and-conquer tasks.


5.1 Work Stealing Model#

graph TD A[Task] --> B[Subtask 1] A --> C[Subtask 2] B --> D[Worker 1] C --> E[Worker 2] E -->|Steal| D

5.2 Example#

import java.util.concurrent.*;

class SumTask extends RecursiveTask<Long> {
    private final long[] array;
    private final int start, end;
    private static final int THRESHOLD = 1000;

    SumTask(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    protected Long compute() {
        if (end - start <= THRESHOLD) {
            long sum = 0;
            for (int i = start; i < end; i++)
                sum += array[i];
            return sum;
        }

        int mid = (start + end) / 2;
        SumTask left = new SumTask(array, start, mid);
        SumTask right = new SumTask(array, mid, end);

        left.fork();
        return right.compute() + left.join();
    }
}

5.3 When to Use#

  • CPU-bound tasks
  • Parallel algorithms
  • Large data processing

Alternative: Parallel Streams


6. CompletableFuture (Async Pipelines)#

Modern non-blocking model.


6.1 Example#

import java.util.concurrent.*;

public class CompletableFutureExample {
    public static void main(String[] args) {
        CompletableFuture
            .supplyAsync(() -> "Hello")
            .thenApply(msg -> msg + " World")
            .thenAccept(System.out::println)
            .join();
    }
}

6.2 Pipeline Model#

graph LR A[SupplyAsync] --> B[Transform] B --> C[Consume]

6.3 Internals#

  • Uses ForkJoinPool (common pool)
  • Non-blocking chaining
  • Completion graph model

6.4 Alternative#

Virtual threads for simpler blocking code.


7. Virtual Threads (JDK 25 LTS)#

Major innovation from Project Loom.


7.1 What Are Virtual Threads?#

  • Lightweight
  • Managed by JVM
  • Thousands can exist
  • Blocking is cheap

7.2 Example#

public class VirtualThreadExample {
    public static void main(String[] args) throws Exception {
        try (var executor =
                 Executors.newVirtualThreadPerTaskExecutor()) {

            for (int i = 0; i < 1000; i++) {
                executor.submit(() -> {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread());
                });
            }
        }
    }
}

7.3 Architecture#

Virtual Thread
      ↓
JVM Scheduler
      ↓
Platform Thread Pool
      ↓
CPU Core

7.4 Why They Matter#

Traditional:

10,000 requests → 10,000 OS threads → Memory pressure

Virtual threads:

10,000 requests → Few OS threads → Efficient scheduling

7.5 When to Use#

  • Web servers
  • Microservices
  • Blocking database calls
  • High concurrency systems

8. Structured Concurrency (Scoped Tasks)#

Provides lifecycle control.


8.1 Example (JDK 25 style)#

import java.util.concurrent.*;

public class StructuredExample {
    public static void main(String[] args) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

            var future1 = scope.fork(() -> fetchUser());
            var future2 = scope.fork(() -> fetchOrders());

            scope.join();
            scope.throwIfFailed();

            System.out.println(future1.get() + " " + future2.get());
        }
    }

    static String fetchUser() { return "User"; }
    static String fetchOrders() { return "Orders"; }
}

8.2 Execution Model#

graph TD P[Parent Task] P --> C1[Child Task 1] P --> C2[Child Task 2] C1 --> P C2 --> P

Benefits:

  • Automatic cancellation
  • Failure propagation
  • Scoped lifetime

9. Parallel Streams#

List<Integer> numbers = List.of(1,2,3,4,5);

numbers.parallelStream()
       .map(n -> n * 2)
       .forEach(System.out::println);

Internally uses ForkJoinPool.

Best for:

  • Stateless transformations
  • Large datasets

10. Comparison Table#

TypeBest ForBlocking FriendlyScales HighComplexity
Platform ThreadCPU tasksNoLimitedLow
Fixed PoolControlled workloadsNoMediumMedium
Fork/JoinCPU parallelismNoHighHigh
CompletableFutureAsync pipelinesPartialHighMedium
Virtual ThreadsI/O workloadsYesVery HighLow
Structured ConcurrencyScoped tasksYesHighMedium

11. Architectural Decision Guide#

CPU-bound:#

→ ForkJoinPool or Fixed Thread Pool

I/O-bound:#

→ Virtual Threads

Complex async graph:#

→ CompletableFuture

Request aggregation:#

→ Structured Concurrency + Virtual Threads


12. Common Concurrency Mistakes#

  • Blocking inside ForkJoinPool
  • Using parallel streams for I/O
  • Ignoring thread pool sizing
  • Sharing mutable state
  • Forgetting proper shutdown

13. Final Thoughts#

Concurrency in modern Java is no longer about choosing between Thread and Runnable.

As of JDK 25 LTS:

  • Virtual Threads redefine scalability.
  • Structured Concurrency improves safety.
  • Fork/Join remains powerful for CPU tasks.
  • Executors still serve bounded parallelism.

Architecturally, the correct model depends on:

  • Workload type (CPU vs I/O)
  • Latency requirements
  • Throughput goals
  • Operational constraints

Mastering these models enables building systems that are scalable, efficient, and maintainable.