Up to this point, we’ve seen how to create virtual threads in simple ways. But in real applications, you often need more control.
For example,
· You may want threads to have readable names (like user-thread-0, user-thread-1) so you can easily identify them during debugging.
· You may want to define custom rules for how threads are created, including what happens if something goes wrong.
To do this, Java gives us a tool called a Thread Factory. Think of a thread factory as a machine that produces threads according to your instructions. Instead of creating each thread manually, you tell the factory:
· Please create virtual threads, name them user-thread-0, user-thread-1, and so on.
· Whenever a new thread is needed, the factory automatically follows your rules.
· When we combine this with an ExecutorService, we can submit tasks for execution without worrying about how threads are created or named. Java handles that part for us.
Find the below working Application.
ExecutorServiceWithThreadFactory.java
package com.sample.app.virtual.threads.creation; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; public class ExecutorServiceWithThreadFactory { static void handleUserRequest() { try { Thread.sleep(2000); // Simulate some work System.out.println("Handled by: " + Thread.currentThread()); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { // Step 1: Create a custom thread factory, Threads named: user-thread-0, // user-thread-1, etc. ThreadFactory factory = Thread.ofVirtual().name("user-thread-", 0).factory(); // Step 2: Create an executor service using the custom factory try (ExecutorService executor = Executors.newThreadPerTaskExecutor(factory)) { // Step 3: Submit tasks for execution executor.submit(ExecutorServiceWithThreadFactory::handleUserRequest); executor.submit(ExecutorServiceWithThreadFactory::handleUserRequest); } // Step 4: Executor shuts down automatically here } }
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
Here:
· We create a ThreadFactory that produces virtual threads with names like user-thread-0, user-thread-1, etc.
· We give this factory to an ExecutorService, which handles running our tasks.
· We submit two tasks (handleUserRequest()) for execution.
· Because we used try-with-resources, the executor shuts down automatically once the block ends, no manual cleanup required.
When you run this program, you’ll see messages showing that each task was handled by a uniquely named virtual thread.
Why This Matters?
· In production systems, you might have thousands of concurrent requests coming in such as user logins, file uploads, or database queries.
· Without clear thread names, debugging becomes messy because all threads look the same. By using a custom factory, you can give them meaningful names, making it easier to trace logs and monitor performance.
· Virtual threads also remove the need to carefully manage thread pools. You just submit tasks, and Java creates lightweight virtual threads behind the scenes, keeping your code clean, readable, and scalable.
In summary:
· A Thread Factory is like a machine that creates threads based on your rules (names, behavior, etc.).
· You can combine a custom thread factory with an ExecutorService for better control over thread creation.
· Custom naming makes debugging and monitoring easier in real world systems.
· Virtual threads simplify concurrency, your code looks sequential but can scale to thousands of tasks.
· Automatic shutdown with try-with-resources means less boilerplate and safer resource management.
Previous Next Home
No comments:
Post a Comment