In Java, virtual threads (introduced in Project Loom) make it easier to write concurrent applications without worrying about the heavy cost of platform threads. The simplest way to create a virtual thread is with Thread.ofVirtual().start(...), but sometimes you need more control like giving threads meaningful names, or setting up exception handlers.
That’s where the Virtual Thread Builder comes in. It works like a factory that can create multiple threads with consistent settings.
VirtualThreadBuilder1.java
package com.sample.app.virtual.threads.creation; public class VirtualThreadBuilder1 { static void handleUserRequest() { try { Thread.sleep(2000); // simulate work System.out.println("Handled by: " + Thread.currentThread()); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { // Create a virtual thread builder with a custom name pattern Thread.Builder builder = Thread.ofVirtual().name("user-thread-", 0); // Start threads using the builder Thread t1 = builder.start(VirtualThreadBuilder1::handleUserRequest); Thread t2 = builder.start(VirtualThreadBuilder1::handleUserRequest); // Wait for both to finish t1.join(); t2.join(); } }
Output
Handled by: VirtualThread[#26,user-thread-0]/runnable@ForkJoinPool-1-worker-1 Handled by: VirtualThread[#28,user-thread-1]/runnable@ForkJoinPool-1-worker-2
Thread.Builder builder = Thread.ofVirtual().name("user-thread-", 0);
We create a builder with a name pattern. This means new threads will automatically be named user-thread-0, user-thread-1, etc.
Thread t1 = builder.start(VirtualThreadBuilder1::handleUserRequest);
Thread t2 = builder.start(VirtualThreadBuilder1::handleUserRequest);
We use the builder to start multiple threads. Each thread runs the same method but gets its own name.
When you’re running thousands of virtual threads, debugging can become messy if all threads have default names like VirtualThread[#]. By naming them meaningfully, you can quickly spot which thread is handling what. Imagine profiling your application, you’ll immediately see user-thread-0 and user-thread-1 instead of just numbers. This helps a lot when analyzing logs or debugging.
Example 1: Adding an Uncaught Exception Handler
Thread.Builder builder = Thread.ofVirtual() .name("api-thread-", 0) .uncaughtExceptionHandler((t, e) -> { System.out.println("Thread " + t.getName() + " crashed: " + e.getMessage()); });
Now, if any thread throws an uncaught exception, you’ll know exactly which one failed.
ThreadBuilderExceptionHadnler.java
package com.sample.app.virtual.threads.creation; public class ThreadBuilderExceptionHadnler { static void handleUserRequest() { int a = 10 /0 ; System.out.println(a); } public static void main(String[] args) throws InterruptedException { // Create a virtual thread builder with a custom name pattern Thread.Builder builder = Thread.ofVirtual() .name("api-thread-", 0) .uncaughtExceptionHandler((t, e) -> { System.out.println("Thread " + t.getName() + " crashed: " + e.getMessage()); }); // Start threads using the builder Thread t1 = builder.start(ThreadBuilderExceptionHadnler::handleUserRequest); Thread t2 = builder.start(ThreadBuilderExceptionHadnler::handleUserRequest); // Wait for both to finish t1.join(); t2.join(); } }
Output
Thread api-thread-0 crashed: / by zero Thread api-thread-1 crashed: / by zero
Example 2: Creating Multiple Threads in a Loop
ThreadBuilderCreateMultipleThreads.java
package com.sample.app.virtual.threads.creation; import java.util.concurrent.TimeUnit; public class ThreadBuilderCreateMultipleThreads { public static void main(String[] args) throws InterruptedException { Thread.Builder builder = Thread.ofVirtual().name("worker-", 0); for (int i = 0; i < 5; i++) { builder.start(() -> { System.out.println("Work done by: " + Thread.currentThread()); }); } TimeUnit.SECONDS.sleep(1); } }
Output
Work done by: VirtualThread[#34,worker-3]/runnable@ForkJoinPool-1-worker-4 Work done by: VirtualThread[#31,worker-2]/runnable@ForkJoinPool-1-worker-3 Work done by: VirtualThread[#28,worker-1]/runnable@ForkJoinPool-1-worker-2 Work done by: VirtualThread[#36,worker-4]/runnable@ForkJoinPool-1-worker-6 Work done by: VirtualThread[#26,worker-0]/runnable@ForkJoinPool-1-worker-1
Points to consider
· A Virtual Thread Builder is useful when you want multiple threads with the same configuration (naming pattern, exception handling, etc.).
· It is not thread safe, so don’t share a single builder across multiple concurrent threads.
· Makes debugging, logging, and profiling easier by giving threads consistent names.
· Great for structured concurrency when handling multiple requests or tasks.
By using a Virtual Thread Builder, you gain flexibility and control without adding complexity. It’s a small step, but it makes your code cleaner, more maintainable, and much easier to debug in real-world applications.
Previous Next Home
No comments:
Post a Comment