Introducing virtual threads 3 – Concurrency – Virtual Threads, Structured Concurrency
Waiting for a virtual task to terminate
The given task is executed by a virtual thread, while the main thread is not blocked. In order to wait for the virtual thread to terminate we have to call one of the join() flavors. We have join() without arguments that waits indefinitely, and a few flavors that wait for a given time (for instance, join(Duration duration), join(long millis)):
vThread.join();
These methods throw an InterruptedException so you have to catch it and handle it or just throw it. Now, because of join(), the main thread cannot terminate before the virtual thread. It has to wait until the virtual thread completes.
Creating an unstarted virtual thread
Creating an unstarted virtual thread can be done via unstarted(Runnable task) as follows:
Thread vThread = Thread.ofVirtual().unstarted(task);
Or, via Thread.Builder as follows:
Thread.Builder builder = Thread.ofVirtual();
Thread vThread = builder.unstarted(task);
This time, the thread is not scheduled for execution. It will be scheduled for execution only after we explicitly call the start() method:
vThread.start();
We can check if a thread is alive (it was started but not terminated) via the isAlive() method:
boolean isalive = vThread.isAlive();
The unstarted() method is available for platform threads as well (there is also the Thread.Builder.OfPlatform subinterface):
Thread pThread = Thread.ofPlatform().unstarted(task);
We can start pThread by calling the start() method.
Creating a ThreadFactory for virtual threads
You can create a ThreadFactory of virtual threads as follows:
ThreadFactory tfVirtual = Thread.ofVirtual().factory();
ThreadFactory tfVirtual = Thread.ofVirtual()
.name(“vt-“, 0).factory(); // ‘vt-‘ name prefix, 0 counter
Or, via Thread.Builder as follows:
Thread.Builder builder = Thread.ofVirtual().name(“vt-“, 0);
ThreadFactory tfVirtual = builder.factory();
And, a ThreadFactory for platform threads as follows (you can use Thread.Builder as well):
ThreadFactory tfPlatform = Thread.ofPlatform()
.name(“pt-“, 0).factory();// ‘pt-‘ name prefix, 0 counter
Or, a ThreadFactory that we can use to switch between virtual/platform threads as follows:
static class SimpleThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
// return new Thread(r); // platform thread
return Thread.ofVirtual().unstarted(r); // virtual thread
}
}
Next, we can use any of these factories via the ThreadFactory#newThread(Runnable task) as follows:
tfVirtual.newThread(task).start();
tfPlatform.newThread(task).start();
SimpleThreadFactory stf = new SimpleThreadFactory();
stf.newThread(task).start();
If the thread factory starts the created thread as well then there is no need to explicitly call the start() method.
Checking a virtual thread details
Moreover, we can check if a certain thread is a platform thread or a virtual thread via isVirtual():
Thread vThread = Thread.ofVirtual()
.name(“my_vThread”).unstarted(task);
Thread pThread1 = Thread.ofPlatform()
.name(“my_pThread”).unstarted(task);
Thread pThread2 = new Thread(() -> {});
logger.info(() -> “Is vThread virtual ? “
+ vThread.isVirtual()); // true
logger.info(() -> “Is pThread1 virtual ? “
+ pThread1.isVirtual()); // false
logger.info(() -> “Is pThread2 virtual ? “
+ pThread2.isVirtual()); // false
Obviously, only vThread is a virtual thread.A virtual thread runs always as a daemon thread. The isDaemon() method returns true, and trying to call setDaemon(false) will throw an exception.The priority of a virtual thread is always NORM_PRIORITY (calling getPriority() always return 5 – constant int for NORM_PRIORITY). Calling setPriority() with a different value has no effect.A virtual thread cannot be part of a thread group because it already belongs to the VirtualThreads group. Calling getThreadGroup().getName() returns VirtualThreads.A virtual thread has no permission with Security Manager (which is deprecated anyway).