Virtual Threads: New Foundations for High-Scale Java Applications
The section &quot;What about async&#x2F;await?&quot;, which compares these virtual threads to async&#x2F;await is very weak. After reading this article, I came away with the impression that this is a dramatically worse way to solve this problem than async&#x2F;await. The only benefit I see is that this will be simpler to use for the (increasingly rare) programmers who are not used to async programming.<p>The first objection in the article is that with async&#x2F;await you to may forget to use an async operation and could instead use a synchronous operation. This is not a real problem. Languages like JavaScript do not have any synchronous operations so you can&#x27;t use them by mistake. Languages like python and C# solve this with simple lint rules that tell you if you make this mistake.<p>The second objection is that you have to reimplement all library functions to support await. This is a bad objection because you also have to do this for virtual threads. Based on how long it took to add virtual threada to Java vs adding async&#x2F;await to other languages, it seems like virtual threads were much more complicated to implement.<p>The programming model here sounds analogous to using gevent with python vs python async&#x2F;await. My opinion is that the gevent approach will die out completely as async&#x2F;await becomes better supported and programmers become more familiar.<p>EDIT: Looking more at the &quot;Related Work&quot; section at the bottom. I think I understand the problem here. The &quot;Structured Concurrency&quot; examples are unergonomical versions of async&#x2F;await. I&#x27;m not sure what I&#x27;m missing but this seems like a strictly worse way to write structured concurrent code.<p>Java example:<p><pre><code>    Response handle() throws ExecutionException, InterruptedException {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            Future&lt;String&gt;  user  = scope.fork(() -&gt; findUser());
            Future&lt;Integer&gt; order = scope.fork(() -&gt; fetchOrder());

            scope.join();           &#x2F;&#x2F; Join both forks
            scope.throwIfFailed();  &#x2F;&#x2F; ... and propagate errors

            &#x2F;&#x2F; Here, both forks have succeeded, so compose their results
            return new Response(user.resultNow(), order.resultNow());
        }
    }
</code></pre>
Python equivalent<p><pre><code>    async def handle() -&gt; Response:
      # scope is implicit, throwing on failure is implicit.
      user, order = await asyncio.gather(findUser(), findOrder())

      return Response(user, order)
</code></pre>
You could probably implement a similar abstraction in Java, but you would need to pass around and manage the the scope object, which seems cumbersome.