I understand that instead of pooling VirtualThreads, 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 Threads 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 ExecutorServices.
No, not all tasks should be performed with virtual threads. Virtual threads are appropriate for tasks that:
synchronizedonly briefly, around small amounts of code that execute quckly. Long chores insidesynchronizedshould either be refactored to use aReentrantLockor else run in a platform thread. (Thissynchronizedlimitation may change in future versions of Java, beyond Java 21.)As others commented, your code does not actually run in a platform thread. Your use of
Threadas aRunnablemeans therunmethod of your task will execute on a new virtual thread in the existing executor service. TheThread#startmethod 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
Threadas aRunnable. Having made theThreadclass implement theRunnableinterface is a flaw in the API, a regrettable design choice.As stated above, effectively you passed a
Runnableto your executor service, not aThread. Your code never started yourThreadobject, never used anyThreadfeature.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
Threaddirectly.Focus on defining your tasks as
Runnable/Callableobjects, 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: