I understand that instead of pooling VirtualThread
s, which does not really bring benefits, a Executors.newVirtualThreadPerTaskExecutor()
should be used in order to wrap all of our tasks with new virtual threads.
I was wondering though if it should be discouraged to execute a PlatformThread
with such an executor, and if generally Thread
s altogether should never be passed to an executor.
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
Runnable platformThread = Thread.ofPlatform().unstarted(someTask);
executor.execute(platformThread);
}
The platform thread created this way will be wrapped with a virtual thread because of the ThreadFactory
of the executor. I am trying to understand if this could ever make sense, or if in general Thread
objects should never be passed to ExecutorService
s.
wrap all of our tasks with new virtual threads.
No, not all tasks should be performed with virtual threads. Virtual threads are appropriate for tasks that:
synchronized
only briefly, around small amounts of code that execute quckly. Long chores inside synchronized
should either be refactored to use a ReentrantLock
or else run in a platform thread. (This synchronized
limitation may change in future versions of Java, beyond Java 21.)
Runnable platformThread = Thread.ofPlatform().unstarted(someTask);
As others commented, your code does not actually run in a platform thread. Your use of Thread
as a Runnable
means the run
method of your task will execute on a new virtual thread in the existing executor service. The Thread#start
method of your object will never be called, and so no platform thread will execute as a result of this code.
You’ve fallen into the trap of using Thread
as a Runnable
. Having made the Thread
class implement the Runnable
interface is a flaw in the API, a regrettable design choice.
in general Thread objects should never be passed to ExecutorServices
As stated above, effectively you passed a Runnable
to your executor service, not a Thread
. Your code never started your Thread
object, never used any Thread
feature.
generally Threads altogether should never be passed to an executor
The entire purpose of the Executors framework is to relieve us Java programmers of the burden of managing threads. In modern Java we rarely deal with Thread
directly.
Focus on defining your tasks as Runnable
/Callable
objects, and submitting them to an executor service. The only time we need to think about threads is in choosing what kind of executor service to establish: virtual versus platform threads, and if platform threads, how many.
Keep in mind that you can maintain more than one executor service at runtime: