Debugging Virtual Threads in Java 21: Tips and Tools

🧩 Introduction

With Java 21, Virtual Threads became a powerful tool to simplify writing scalable concurrent applications. But what happens when things go wrong? How do you debug issues in these lightweight threads?

This blog will walk you through the ways of debugging virtual threads using simple examples, helpful tools, and clear explanations that even beginners can follow.

Debugging Virtual Threads in Java 21: Tips and Tools

🎯 What Are Virtual Threads?

Virtual threads are a lightweight version of platform threads. They are:

  • Managed by the Java runtime (not the OS)
  • Created in huge numbers without harming performance
  • Great for handling concurrent tasks

To use them, Java 21 provides the Thread.ofVirtual() API.

You can go through Introduction to Virtual Threads: Java 21’s Game-Changer for Concurrency for understanding the basic’s.

πŸ”§ Why Debugging Virtual Threads Is Different

Traditional debuggers were made for heavyweight threads. With virtual threads:

  • There can be thousands of threads
  • Stack traces may look different
  • Tools may not always display virtual threads by default

But don’t worry β€” there are ways to handle this!

πŸ—‚οΈ Sample Project Structure

We’ll use this package structure:

πŸ‘¨β€πŸ’» Step-by-Step Example

File : Service.java

File : Main.java

This code runs 5 tasks in virtual threads, each calling a simple method.

πŸ•΅οΈβ€β™‚οΈ How to Debug Virtual Threads

βœ… 1. Use IntelliJ IDEA (2023.1+) or VS Code

Modern IDEs like IntelliJ and Visual Studio Code now support debugging virtual threads. Make sure you’re using:

  • IntelliJ IDEA 2023.1 or newer
  • Java 21 SDK
  • Enable “Show all threads” in debugger

βœ… 2. Print Thread Info in Logs

Always print the current thread in your logs:

This helps you trace actions done by each virtual thread.

βœ… 3. Use StackWalker API

Java’s StackWalker helps inspect stack traces inside a running thread:

This is useful when debugging without an IDE or to trace thread stacks during execution.

βœ… 4. Add Thread Names (Optional)

To name threads for better logs:

Example output:

Running task 1 in thread: VirtualThread[ #29 , worker-1]

βœ… 5. Using JDK Mission Control + Java Flight Recorder

JFR and JMC support virtual threads starting in Java 21.

  • Add -XX:StartFlightRecording when starting your app
  • Analyze in JDK Mission Control

This helps visualize thread behavior and performance over time.

πŸ” Common Issues & Fixes

IssueSolution
Logs show many unnamed threadsUse .name("...") when creating virtual threads
IDE doesn’t show threadsUpdate IDE & JDK; enable all threads in settings
Threads not working as expectedCheck try-with-resources in executors

πŸ§ͺ Bonus: Debugging with Breakpoints

You can still set breakpoints like usual in:

Even though thousands of threads may run, IDEs like IntelliJ will pause only the one that hits the breakpoint.

βœ… Works just like platform threads!

🧹 Clean Up After Debugging

Remember to close your virtual thread executors:

This ensures no hanging threads are left in your app.

🧠 Summary

Debugging virtual threads might feel new, but it’s manageable with the right tools and techniques:

βœ… Use Thread.currentThread() for clarity
βœ… Use modern IDEs and enable all-thread views
βœ… Leverage structured logging and JFR for observability
βœ… Don’t forget thread naming and try-with-resources cleanup