Non-obvious behavior in newVirtualThreadPerTaskExecutor

Robert Engels robaho at me.com
Mon Dec 1 23:13:25 UTC 2025


Hi,

But don’t you have to use interrupts - that is the only way to force a return from many if not all system calls? - except for socket calls where I think you need to close the socket to interrupt a blocking read.

> On Dec 1, 2025, at 4:48 PM, Attila Kelemen <attila.kelemen85 at gmail.com> wrote:
> 
> In my opinion, the core of the issue is not the behavior of close (if it did shutdownNow, then there are other nasty things which could happen, and would be equally surprising, but in other circumstances). The root of the problem is that thread interruption is a poor way to manage cancellation (they suffer from the same issue as global variables do). This poor cancellation mechanism was the main reason I created my own executor framework a long time ago. I wish Java never tried to use thread interrupts for cancellation.
> 
> Stig Rohde Døssing <stigdoessing at gmail.com <mailto:stigdoessing at gmail.com>> ezt írta (időpont: 2025. dec. 1., H, 21:17):
>> Hi,
>> 
>> I stumbled on a minor landmine with newVirtualThreadPerTaskExecutor, and figured I'd share in case something can be done to communicate this behavior a little better, because it caught me by surprise.
>> 
>> JEP 444 suggests code like the following
>> 
>> try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
>>         var future1 = executor.submit(() -> fetchURL(url1));
>>         var future2 = executor.submit(() -> fetchURL(url2));
>>         response.send(future1.get() + future2.get());
>> } catch (ExecutionException | InterruptedException e) {
>>         response.fail(e);
>> }
>> 
>> This led me to believe that I could write code like that, and get something that reacts to interrupts promptly. But unfortunately, the close method on this executor does not interrupt the two submitted fetches. Instead, if the main thread in this code is interrupted, the thread will block until the two fetches complete on their own time, and then it will fail the request, which seems a little silly. 
>> 
>> Due to how try-with-resources works, the executor is closed before the catch block is hit, so in order to "stop early" when interrupted, you'd need to add a nested try-catch inside the try-with-resources to either interrupt the two futures or call shutdownNow on the executor when the main thread is interrupted.
>> 
>> I know that the structured concurrency API will be a better fit for this kind of thing, but given that virtual threads are likely to spend a lot of their time blocking waiting for something to occur, it seems a little unfortunate that the ThreadPerTaskExecutor for virtual threads doesn't do shutdownNow on close when used in the most straightforward way.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20251201/1cdf5392/attachment.htm>


More information about the loom-dev mailing list