Executors.newVirtualThreadPerTaskScheduledExecutor() ?

Dr Heinz M. Kabutz heinz at javaspecialists.eu
Wed Aug 2 13:47:35 UTC 2023


>> On 2 Aug 2023, at 12:26, Dr Heinz M. Kabutz<heinz at javaspecialists.eu>  wrote:
>>
>> I've been doing this for a while (and am using the approach on my javaspecialists.eu website):
>> Executors.newSingleThreadScheduledExecutor(
>> Thread.ofVirtual().factory())
>>
>> It works very well indeed.
>> Regards
>>
>> Heinz
>>> I would advise against that and in favour of the approach in the Stack Overflow answer. Remember that a virtual thread should normally only ever execute a single task that’s been submitted to an Executor.
>
> I understand that your intent is good, and that you probably just want to use the convenient “fixed rate” methods of ScheduledExecutorService to run what is *conceptually* a single task, but many of the problems we see with people misusing virtual threads and not getting good results are due to them not internalising this idea that a virtual thread is a single task, so I would suggest not to show this kind of usage to virtual thread beginners.
>
> — Ron

What about this use case for virtual threads?

         Cleaner cleaner = Cleaner.create(Thread.ofVirtual().factory());

(Not that I typically need Cleaners, but if I did, I wouldn't want a 
platform thread bound up just for that.)

Could you perhaps expand a bit on the problems that are caused by my 
solution above? I can think of a few, but not sure if that is what you 
meant. For example:

1. One task using a lot of CPU and thus preventing the unmounting of a 
carrier thread (this would be the same with the SOF solution - actually 
their approach may be even worse, because we could have successive tasks 
that run and overlap each other).

2. Virtual carrier threads are daemons, so the timer would not prevent 
the JVM from shutting down. However, I would typically make timers to 
use daemon threads anyway.

What else do you see going wrong?

Here is an experiment to demonstrate point 1, using the donkey approach 
from SOF. Excuse the copy & paste code please - could do with some 
refactoring:

import java.time.*; import java.util.concurrent.*; public class VirtualTimers {
     public static void main(String... args)throws InterruptedException {
         test("platform thread timer", Executors.newSingleThreadScheduledExecutor()); test("virtual thread timer", Executors.newSingleThreadScheduledExecutor(Thread.ofVirtual().factory())); test2("virtual thread timer with donkey", Executors.newSingleThreadScheduledExecutor(), Executors.newVirtualThreadPerTaskExecutor()); }

     private static void test(String description, ScheduledExecutorService timer)
             throws InterruptedException {
         System.out.println(description); try (timer) {
             timer.scheduleWithFixedDelay(new Runnable() {
                 private int counter; public void run() {
                     System.out.println("Starting task " + ++counter); try {
                         Thread.sleep(Duration.ofMillis(1500)); }catch (InterruptedException e) {
                         throw new CancellationException("interrupted"); }
                     System.out.println("Finished task " +counter); }
             }, 1, 1, TimeUnit.SECONDS); Thread.sleep(Duration.ofSeconds(10)); }
         System.out.println(); }
     
     private static void test2(String description, ScheduledExecutorService timer, ExecutorService virtualThreadService)
             throws InterruptedException {
         System.out.println(description); try (timer; virtualThreadService) {
             Runnable task =new Runnable() {
                 private int counter; public void run() {
                     System.out.println("Starting task " + ++counter); try {
                         Thread.sleep(Duration.ofMillis(1500)); }catch (InterruptedException e) {
                         throw new CancellationException("interrupted"); }
                     System.out.println("Finished task " +counter); }
             }; timer.scheduleWithFixedDelay(() ->virtualThreadService.execute(task), 1, 1, TimeUnit.SECONDS); Thread.sleep(Duration.ofSeconds(10)); }
         System.out.println(); }
}

Not only do we see the tasks overlapping each other, but we also have to 
now ensure that counter is thread-safe.

platform thread timer
Starting task 1
Finished task 1
Starting task 2
Finished task 2
Starting task 3
Finished task 3
Starting task 4
Finished task 4

virtual thread timer
Starting task 1
Finished task 1
Starting task 2
Finished task 2
Starting task 3
Finished task 3
Starting task 4
Finished task 4

virtual thread timer with donkey
Starting task 1
Starting task 2
Finished task 2
Starting task 3
Finished task 3
Starting task 4
Finished task 4
Starting task 5
Finished task 5
Starting task 6
Finished task 6
Starting task 7
Finished task 7
Starting task 8
Finished task 8
Starting task 9
Finished task 9
Finished task 9



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20230802/94089ee9/attachment-0001.htm>


More information about the loom-dev mailing list