Wednesday, July 23, 2025

Java 21 Virtual Threads Basic and Foundation

Introduction

Hey everyone ๐Ÿ‘‹,

Welcome to the first post in our deep dive series on Java Virtual Threads! Before we jump into the nuts and bolts of virtual threads, it’s important to lay the foundation. Yes, the first couple of sections may feel basic—but they are essential to understand how things work under the hood, especially when we eventually zoom in on virtual threads.

So bear with me. This foundational knowledge will help you fully grasp the power and potential of virtual threads.

๐Ÿš€ Starting Simple: Running a Java Program

Let’s imagine you’ve developed a simple Java application and packaged it into a JAR file.

 java -jar myapp.jar


When you run this command, your program is loaded into memory and a process is created.

๐Ÿง  What is a Process?


A process is an instance of a running program. It comes with:
  • Its own isolated memory space (heap + stack)
  • Code and data
  • OS-allocated resources like memory, sockets, file handles, etc.

 ๐Ÿ’ก Note: A process is considered heavyweight. Creating or destroying a process is expensive in terms of performance.

๐Ÿงต What is a Thread?


A thread is the small unit of execution within a process. Every process has at least one thread and can have multiple threads.

๐Ÿง  Think of it this way:

Process = Unit of Resources

Thread = Unit of Execution

Each thread within a process can share memory with the other threads in that same process.

๐Ÿ“ Java Process vs Thread (Comparison Table)


 | Feature        | Java Process      | Java Thread                                   |
 | -------------- | ----------------- | --------------------------------------------- |
 | Memory         | Own memory space  | Shares memory with other threads              |
 | Weight         | Heavyweight       | Relatively lightweight                        |
 | Execution Unit | Program container | Code execution unit                           |
 | OS Mapping     | One process       | Mapped to one OS thread (pre-virtual threads) |
 

๐Ÿ”„ Thread Scheduling by the OS


Now, let’s introduce the CPU and OS scheduler into the picture.

Let’s say we have only one CPU core. Here’s how execution works:

The OS scheduler picks a thread and lets it run.

After some time, it switches to another thread.

This switching is called a context switch.

 Thread A ➡️ CPU executes for X ms
 Thread B ➡️ CPU executes for Y ms
 ...
 
If you have multiple cores, threads can run in parallel across different cores.

 ๐Ÿ’ก Important: Switching between threads involves storing the thread's state (function call, local variables, etc.) so it can resume later. This is what makes context switching expensive.

 

๐Ÿ”„ What Is Context Switching?

When the CPU switches from one thread to another, it must save the current thread's state and load the new one. This is called a context switch, and it’s expensive!


๐Ÿงฐ The Java Thread Model


Java introduced threads over two decades ago. But here’s the catch:

A Java thread is just a wrapper around an OS thread.

Each Java thread directly maps to a kernel thread.
 public class MyThread extends Thread {
     public void run() {
         // Your logic here
     }
 }
 

So whenever you create a thread in Java, you’re actually relying on the underlying OS thread.


๐Ÿงฑ Stack vs Heap Memory


Each Java thread has its own stack memory, while the process shares a heap memory.

 ๐Ÿ“Œ Fun fact: The stack size is fixed per thread (default is 1MB in Java, 2MB in macOS).

 | Memory Type | Purpose                                                       |
 | ----------- | ------------------------------------------------------------- |
 | Stack       | Stores method calls, local variables                          |
 | Heap        | Stores dynamically created objects (ArrayList, HashMap, etc.) |
 


 void method1() {
     int x = 10;
     method2();
 }

 void method2() {
     String name = "Java";
 }
 

⚠️ So Where's the Problem?


Let’s take a look at modern applications.

In a microservices architecture, every request can lead to multiple network calls:

OrderService → UserService

UserService → PaymentService

PaymentService → Third-party API

Every service → Their own database

๐Ÿงต A thread that starts the request must wait (idle) while these calls happen. But...

๐Ÿ”ฅ We're blocking an expensive OS thread while it's doing nothing!

This is inefficient, especially when we need to handle thousands of concurrent requests.


๐Ÿ†š Traditional Threads vs Virtual Threads



 | Feature             | Traditional Threads       | Virtual Threads              |
 | ------------------- | ------------------------- | ---------------------------- |
 | Backed by OS thread | ✅ Yes                     | ❌ No                         |
 | Creation overhead   | High (expensive)          | Low (lightweight)            |
 | Memory usage        | \~1MB per thread          | Few KB per thread            |
 | Blocking behavior   | Blocks OS thread          | Suspends virtual thread only |
 | Scalability         | Limited by system threads | Virtually unlimited          |
 


✅ Enter Virtual Threads


That’s where virtual threads come in. These are not OS threads. So how do they execute?

That’s what we’ll explore in the coming parts of this series:

  • How virtual threads are scheduled
  • How they avoid blocking OS threads
  • Their memory usage and performance benefits
  • Integration with existing frameworks like Spring

๐Ÿ› ️ Stay tuned—we’ll break it all down with examples, diagrams, and real-world use cases!

References

No comments:

Post a Comment

Please do not add any spam links in the comments section.