eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

eBook – Java Concurrency – NPI (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

1. Overview

In this tutorial, we’ll take a quick look at Project Loom. In essence, the primary goal of Project Loom is to explore, incubate, and deliver Java VM features and APIs built on top of them for the purpose of supporting easy-to-use, high-throughput lightweight concurrency and new programming models on the Java platform.

2. Project Loom

Project Loom is an attempt by the OpenJDK community to introduce a lightweight concurrency construct to Java. At the outset, project Loom envisages introducing lightweight concurrency pervasively via new features, APIs, and optimizations across the whole JDK. The prototypes for Loom have introduced a change in the JVM and the Java library. The main feature of Project Loom is virtual threads, and it has already been implemented.

Although there is no scheduled release for a JDK version that completely implements Loom yet, we can access the Project Loom Early-Access Builds.

Before we discuss the various concepts of Loom, let’s discuss the current concurrency model in Java.

3. Java’s Concurrency Model

Presently, Thread represents the core abstraction of concurrency in Java. This abstraction and other concurrent APIs make it easy to write concurrent applications. To elaborate, we use Thread to create platform threads that are typically mapped 1:1 to operating system kernel threads. The operating system allocates a large stack and other resources to platform threads; however, these resources are limited. Nevertheless, we use platform threads for executing all types of tasks.

However, since Java uses the OS kernel threads for the implementation, it fails to meet today’s concurrency requirements. There are two major problems in particular:

  1. Threads cannot match the scale of the domain’s unit of concurrency. For example, applications usually allow up to millions of transactions, users, or sessions. However, the number of threads supported by the kernel is much less. Thus, a Thread for every user, transaction, or session is often not feasible.
  2. Most concurrent applications need some synchronization between threads for every request. Due to this, an expensive context switch happens between OS threads.

A possible solution to such problems is the use of asynchronous concurrent APIs. Common examples are CompletableFuture and RxJava. Provided that such APIs don’t block the kernel thread, it gives an application a finer-grained concurrency construct on top of Java threads.

On the other hand, such APIs are harder to debug and integrate with legacy APIs. Thus, there is a need for a lightweight concurrency construct that is independent of kernel threads.

4. Tasks and Schedulers

Any implementation of a thread, either lightweight or heavyweight, depends on two constructs:

  1. Task (also known as a continuation) – A sequence of instructions that can suspend itself for some blocking operation
  2. Scheduler – For assigning the continuation to the CPU and reassigning the CPU from a paused continuation

Presently, Java relies on OS implementations for both the continuation and the scheduler.

Now, in order to suspend a continuation, it’s required to store the entire call stack. And similarly, retrieve the call stack on resumption. Since the OS implementation of continuations includes the native call stack along with Java’s call stack, it results in a heavy footprint.

A bigger problem, though, is the use of an OS scheduler. Since the scheduler runs in kernel mode, there’s no differentiation between threads. And it treats every CPU request in the same manner.

This type of scheduling is not optimal for Java applications in particular.

For example, consider an application thread that performs some action on the requests and then passes on the data to another thread for further processing. Here, it would be better to schedule both these threads on the same CPU. However, since the scheduler is agnostic to the thread requesting the CPU, this is impossible to guarantee.

Project Loom proposes to solve this through user-mode threads which rely on Java runtime implementation of continuations and schedulers instead of the OS implementation.

5. Virtual Threads

OpenJDK 21 introduced virtual threads, along with a provision to create them in the existing API (Thread and ThreadFactory).

5.1. How Are Virtual Threads Different?

Platform threads and virtual threads are different in that the latter are typically user-mode threads, along with other differences:

  • Scheduling – Virtual threads are scheduled by the Java runtime rather than the operating system
  • User-mode – Virtual threads wrap any task in an internal user-mode continuation. This allows the task to be suspended and resumed in Java runtime instead of the kernel
  • Naming – Virtual threads do not require, or have a thread name by default; however, we can set a name
  • Thread Priority – Virtual threads have a fixed thread priority that we can’t change
  • Daemon Threads – Virtual threads are daemon threads; therefore, they don’t prevent the shutdown sequence

5.2. What Are the Pros/Cons of Virtual Threads?

Virtual threads have their pros and cons:

Pros Cons
Virtual threads are lightweight. As lightweight threads, they are not suitable for CPU-bound tasks.
Virtual threads can be created by the user. Many virtual threads share the same operating system thread. Virtual threads block in constructs involving synchronized methods and statements because virtual threads are pinned to their underlying platform threads.
We can readily create virtual threads when we need them. Project Loom developers have to modify every API in the JDK that uses threads so that it can be seamlessly used with virtual threads.
Virtual threads typically require few resources. As an example, a single JVM can support millions of virtual threads. Thread-local variables would require a lot more memory if each of a million virtual threads had its copy of thread-local variables.

5.3. When to Use Virtual Threads?

We can use virtual threads when we want to execute tasks that spend most of their time blocked. We use lightweight, user-mode virtual threads instead of platform threads for tasks that are mostly waiting for I/O operations to complete.

However, we shouldn’t use virtual threads for long-running CPU-intensive operations.

5.4. How to Create Virtual Threads?

We have two main options for creating virtual threads. The Thread class adds a new class method called ofVirtual that returns a builder for creating a virtual Thread or ThreadFactory that creates virtual threads.

Accordingly, we can start a virtual thread to run a task:

Thread thread = Thread.ofVirtual().start(Runnable task);

Alternatively, we can use the equivalent form to create a virtual thread to execute a task and schedule it to run:

Thread thread = Thread.startVirtualThread(Runnable task);

Furthermore, we can use a ThreadFactory that creates virtual threads:

ThreadFactory factory = Thread.ofVirtual().factory();
Thread thread = factory.newThread(Runnable task);

We can use the isVirtual() method to find if a thread is virtual:

boolean isThreadVirtual = thread.isVirtual();

A thread is virtual if this method returns true.

5.5. How Are Virtual Threads Implemented?

Virtual threads are implemented using a small set of underlying platform threads called carrier threads. Operations, such as I/O operations, can reschedule a carrier thread from one virtual thread to another. However, the code running in a virtual thread is not aware of the underlying platform thread. Accordingly, the currentThread() method returns the Thread object for the virtual thread and not the underlying platform thread.

Let’s go through some other optimizations for lightweight concurrency.

6. Delimited Continuations

A continuation (or co-routine) is a sequence of instructions that executes sequentially and that can yield and be resumed by the caller at a later stage.

Every continuation has an entry point and a yield point. The yield point is where it was suspended. Whenever the caller resumes the continuation, the control returns to the last yield point.

It’s important to realize that this suspend/resume now occurs in the language runtime instead of the OS. Therefore, it prevents the expensive context switch between kernel threads.

Delimited continuations are added to support virtual threads; therefore, they don’t need to be exposed as a public API. Let’s discuss a delimited continuation, which is essentially a sequential sub-program with an entry point, with a pseudo-code example. We can create a continuation in the main() with an entry point as one().

Subsequently, we can invoke the continuation, which passes control to the entry point. The one() may call other sub-routines, for example, two(). Execution is suspended in two(), which passes control outside of the continuation and the first invocation of continuation in main() returns.

Let’s invoke the continuation in main() to resume, which passes control to the last suspension point. All of this happens within the same execution context:

one() {  
    ... 
    two()
    ...
}
​
two() {
    ...
    suspend //  suspension point
    ... // resume point
}
​
main() {
    c = continuation(one) // create continuation  
    c.continue() // invoke continuation 
    c.continue() // invoke continuation again to resume
}

For stackful continuations, such as the one we discussed, the JVM needs to capture, store, and resume callstacks not as part of kernel threads. To add to the JVM the ability to manipulate call stacks, unwind-and-invoke (UAI) is a goal of this project. UAI allows unwinding the stack to some point and then invoking a method with given arguments.

7. ForkJoinPool & Custom Schedulers Support in Virtual Threads

Earlier, we discussed the shortcomings of the OS scheduler in scheduling relatable threads on the same CPU.

Although it’s a goal for Project Loom to allow pluggable schedulers with virtual threads, ForkJoinPool in asynchronous mode will be used as the default scheduler. OpenJDK 19 added several new enhancements to the ForkJoinPool class including setParallelism(int size) to set target parallelism, thus controlling the future creation, use, and termination of worker threads.

ForkJoinPool works on the work-stealing algorithm. Thus, every thread maintains a task deque and executes the task from its head. Furthermore, any idle thread does not block, waiting for the task, and pulls it from the tail of another thread’s deque instead.

The only difference in asynchronous mode is that the worker threads steal the task from the head of another deque.

ForkJoinPool adds a task scheduled by another running task to the local queue. Hence, executing it on the same CPU.

8. Structured Concurrency

The OpenJDK has introduced a Preview feature for structured concurrency that falls within the purview of project Loom. The objective of structured concurrency is to treat groups of related tasks running in different threads as a single unit of work, with a single scope. Its benefit is that it streamlines error handling and cancellation, and thus improves reliability, and observability.

For this purpose, it introduces the preview API java.util.concurrent.StructuredTaskScope, which splits a task into multiple, concurrent subtasks. Further, the main task must wait for the subtasks to complete. Using the fork() method we can start new threads to run sub-task, and the join() method to wait for the threads to finish. This API is designed to be used within a try-with-resources statement, as an example:

Callable<String> task1 = ...
Callable<String> task2 = ...

    try (var scope = new StructuredTaskScope<String>()) {

        Subtask<String> subtask1 = scope.fork(task1); //create thread to run first subtask
        Subtask<String> subtask2 = scope.fork(task2); //create thread to run second subtask

        scope.join(); //wait for subtasks to finish

        // process results of subtasks

    }

Afterward, we can process the results of the subtasks.

9. Conclusion

In this article, we discussed the problems in Java’s current concurrency model and the changes proposed by Project Loom.

In doing so, we discussed how lightweight virtual threads introduced in OpenJDK 21 provide an alternative to Java using kernel threads.

Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook – Java Concurrency – NPI (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook Jackson – NPI EA – 3 (cat = Jackson)