<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<blockquote type="cite"
cite="mid:2F8B19AF-764A-46AF-90FB-A6A76E7EDFCC@oracle.com">
<blockquote type="cite">
<pre class="moz-quote-pre" wrap="">On 2 Aug 2023, at 12:26, Dr Heinz M. Kabutz <a class="moz-txt-link-rfc2396E" href="mailto:heinz@javaspecialists.eu"><heinz@javaspecialists.eu></a> 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
—
</pre>
</blockquote>
<pre class="moz-quote-pre" wrap="">
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
</pre>
</blockquote>
<p>What about this use case for virtual threads?</p>
<p> Cleaner cleaner =
Cleaner.create(Thread.ofVirtual().factory());<br>
</p>
<p>(Not that I typically need Cleaners, but if I did, I wouldn't
want a platform thread bound up just for that.)</p>
<p>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:<br>
</p>
<p>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).</p>
<p>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.</p>
<p>What else do you see going wrong?</p>
<p>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:</p>
<div style="background-color:#2b2b2b;color:#a9b7c6">
<pre style="font-family:'JetBrains Mono NL',monospace;font-size:30,0pt;"><span style="color:#cc7832;">import </span>java.time.*<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">import </span>java.util.concurrent.*<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;">public class </span>VirtualTimers {
<span style="color:#cc7832;">public static void </span><span style="color:#ffc66d;">main</span>(String... args) <span style="color:#cc7832;">throws </span>InterruptedException {
<span style="font-style:italic;">test</span>(<span style="color:#6a8759;">"platform thread timer"</span><span style="color:#cc7832;">, </span>Executors.<span style="font-style:italic;">newSingleThreadScheduledExecutor</span>())<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span><span style="font-style:italic;">test</span>(<span style="color:#6a8759;">"virtual thread timer"</span><span style="color:#cc7832;">, </span>Executors.<span style="font-style:italic;">newSingleThreadScheduledExecutor</span>(Thread.<span style="font-style:italic;">ofVirtual</span>().factory()))<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span><span style="font-style:italic;">test2</span>(<span style="color:#6a8759;">"virtual thread timer with donkey"</span><span style="color:#cc7832;">, </span>Executors.<span style="font-style:italic;">newSingleThreadScheduledExecutor</span>()<span style="color:#cc7832;">,
</span><span style="color:#cc7832;"> </span>Executors.<span style="font-style:italic;">newVirtualThreadPerTaskExecutor</span>())<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
<span style="color:#cc7832;">private static void </span><span style="color:#ffc66d;">test</span>(String description<span style="color:#cc7832;">, </span>ScheduledExecutorService timer)
<span style="color:#cc7832;">throws </span>InterruptedException {
System.<span style="color:#9876aa;font-style:italic;">out</span>.println(description)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> try </span>(timer) {
timer.scheduleWithFixedDelay(<span style="color:#cc7832;">new </span>Runnable() {
<span style="color:#cc7832;">private int </span><span style="color:#9876aa;">counter</span><span style="color:#cc7832;">;
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;"> public void </span><span style="color:#ffc66d;">run</span>() {
System.<span style="color:#9876aa;font-style:italic;">out</span>.println(<span style="color:#6a8759;">"Starting task " </span>+ ++<span style="color:#9876aa;">counter</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> try </span>{
Thread.<span style="font-style:italic;">sleep</span>(Duration.<span style="font-style:italic;">ofMillis</span>(<span style="color:#6897bb;">1500</span>))<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>} <span style="color:#cc7832;">catch </span>(InterruptedException e) {
<span style="color:#cc7832;">throw new </span>CancellationException(<span style="color:#6a8759;">"interrupted"</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
System.<span style="color:#9876aa;font-style:italic;">out</span>.println(<span style="color:#6a8759;">"Finished task " </span>+ <span style="color:#9876aa;">counter</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
}<span style="color:#cc7832;">, </span><span style="color:#6897bb;">1</span><span style="color:#cc7832;">, </span><span style="color:#6897bb;">1</span><span style="color:#cc7832;">, </span>TimeUnit.<span style="color:#9876aa;font-style:italic;">SECONDS</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;"> </span>Thread.<span style="font-style:italic;">sleep</span>(Duration.<span style="font-style:italic;">ofSeconds</span>(<span style="color:#6897bb;">10</span>))<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
System.<span style="color:#9876aa;font-style:italic;">out</span>.println()<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
<span style="color:#cc7832;">private static void </span><span style="color:#ffc66d;">test2</span>(String description<span style="color:#cc7832;">, </span>ScheduledExecutorService timer<span style="color:#cc7832;">, </span>ExecutorService virtualThreadService)
<span style="color:#cc7832;">throws </span>InterruptedException {
System.<span style="color:#9876aa;font-style:italic;">out</span>.println(description)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> try </span>(timer<span style="color:#cc7832;">; </span>virtualThreadService) {
Runnable task = <span style="color:#cc7832;">new </span>Runnable() {
<span style="color:#cc7832;">private int </span><span style="color:#9876aa;">counter</span><span style="color:#cc7832;">;
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;"> public void </span><span style="color:#ffc66d;">run</span>() {
System.<span style="color:#9876aa;font-style:italic;">out</span>.println(<span style="color:#6a8759;">"Starting task " </span>+ ++<span style="color:#9876aa;">counter</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> try </span>{
Thread.<span style="font-style:italic;">sleep</span>(Duration.<span style="font-style:italic;">ofMillis</span>(<span style="color:#6897bb;">1500</span>))<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>} <span style="color:#cc7832;">catch </span>(InterruptedException e) {
<span style="color:#cc7832;">throw new </span>CancellationException(<span style="color:#6a8759;">"interrupted"</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
System.<span style="color:#9876aa;font-style:italic;">out</span>.println(<span style="color:#6a8759;">"Finished task " </span>+ <span style="color:#9876aa;">counter</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
}<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>timer.scheduleWithFixedDelay(() -> <span style="color:#b389c5;">virtualThreadService</span>.execute(<span style="color:#b389c5;">task</span>)<span style="color:#cc7832;">,
</span><span style="color:#cc7832;"> </span><span style="color:#6897bb;">1</span><span style="color:#cc7832;">, </span><span style="color:#6897bb;">1</span><span style="color:#cc7832;">, </span>TimeUnit.<span style="color:#9876aa;font-style:italic;">SECONDS</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;"> </span>Thread.<span style="font-style:italic;">sleep</span>(Duration.<span style="font-style:italic;">ofSeconds</span>(<span style="color:#6897bb;">10</span>))<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
System.<span style="color:#9876aa;font-style:italic;">out</span>.println()<span style="color:#cc7832;">;
</span><span style="color:#cc7832;"> </span>}
}
</pre>
</div>
<p></p>
<p>Not only do we see the tasks overlapping each other, but we also
have to now ensure that counter is thread-safe.</p>
<p>platform thread timer<br>
Starting task 1<br>
Finished task 1<br>
Starting task 2<br>
Finished task 2<br>
Starting task 3<br>
Finished task 3<br>
Starting task 4<br>
Finished task 4<br>
<br>
virtual thread timer<br>
Starting task 1<br>
Finished task 1<br>
Starting task 2<br>
Finished task 2<br>
Starting task 3<br>
Finished task 3<br>
Starting task 4<br>
Finished task 4<br>
<br>
virtual thread timer with donkey<br>
Starting task 1<br>
Starting task 2<br>
Finished task 2<br>
Starting task 3<br>
Finished task 3<br>
Starting task 4<br>
Finished task 4<br>
Starting task 5<br>
Finished task 5<br>
Starting task 6<br>
Finished task 6<br>
Starting task 7<br>
Finished task 7<br>
Starting task 8<br>
Finished task 8<br>
Starting task 9<br>
Finished task 9<br>
Finished task 9<br>
<br>
</p>
<p><br>
</p>
<p><br>
</p>
<p><br>
</p>
</body>
</html>