<div dir="ltr"><div>Hi Viktor,</div><div><br></div><div>You are right, the documentation already outlines this behavior, and reading the try-with-resources specification makes the rest clear. It is just an unfortunate example in JEP 444. </div><div><br></div><div>If the main thread is only sent a single interrupt, then by the time ExecutorService.close is called, the main thread will no longer be interrupted, since that flag is cleared when the InterruptedException is thrown.</div><div><br></div><div>This means that if you send the main thread a single interrupt, the code ends up blocking for just as long as it would have otherwise, the only effect the interrupt has is to cause the request to fail, but that failure happens "late" only after the two fetches have completed normally. That's not the kind of behavior I think you'd want from this kind of code, you likely would want those two fetches to be cancelled, so it was surprising to me.</div><div><br></div><div>The only way to get this code to actually stop early is to have an external source send repeated interrupts to the main thread, such that the main thread will be interrupted twice: Once to leave future.get, and once to cause ExecutorService.close to interrupt the tasks.</div><div><br></div><div>But I think it's just a slightly unclear example, and it's possible to work around by creating a wrapping ExecutorService that invokes shutdownNow on close.</div><div><br></div><div>Thanks for your answer, and you are right, we likely want to use SC instead of this executor.</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">Den man. 1. dec. 2025 kl. 22.19 skrev Viktor Klang <<a href="mailto:viktor.klang@oracle.com">viktor.klang@oracle.com</a>>:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>

  
  <div>
    <p>Hi Stig,<br>
    </p>
    <p><b>Executors.newVirtualThreadPerTaskExecutor()</b>'s Javadoc
      states:<br>
      <br>
      <i>"Creates an Executor that starts a new virtual Thread for each
        task. The number of threads created by the Executor is
        unbounded.<br>
        <b>This method is equivalent to invoking
          newThreadPerTaskExecutor(ThreadFactory) with a thread factory
          that creates virtual threads.</b>"</i><br>
      <br>
      -
<a href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/Executors.html#newVirtualThreadPerTaskExecutor()" target="_blank">https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/Executors.html#newVirtualThreadPerTaskExecutor()</a><br>
    </p>
    <p>And if we look at how `ExecutorService::close()` is specified:<br>
      <br>
      <i>"<b>Initiates an orderly shutdown in which previously submitted
          tasks are executed</b>, but no new tasks will be accepted. <b>This
          method waits until all tasks have completed execution and the
          executor has terminated.</b>"</i></p>
    <p>-
<a href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/ExecutorService.html#close()" target="_blank">https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/ExecutorService.html#close()</a><br>
      <br>
      >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. <br>
    </p>
    <p>No, the specification for ExecutorService::close() further
      states:<br>
      <br>
      <i>"<b>If interrupted while waiting, this method stops all
          executing tasks as if by invoking shutdownNow().</b> It then
        continues to wait until all actively executing tasks have
        completed. Tasks that were awaiting execution are not executed.
        The interrupt status will be re-asserted before this method
        returns."<br>
      </i><br>
    </p>
    <div>Given the above: It looks like you want
      to use Structured Concurrency instead of
      Executors.newVirtualThreadPerTaskExecutor()</div>
    <div><br>
    </div>
    <div>On 2025-12-01 21:16, Stig Rohde Døssing
      wrote:<br>
    </div>
    <blockquote type="cite">
      
      <div dir="ltr">
        <div>Hi,</div>
        <div><br>
        </div>
        <div>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.</div>
        <div><br>
        </div>
        <div>JEP 444 suggests code like the following<br>
          <br>
          try (var executor =
          Executors.newVirtualThreadPerTaskExecutor()) {<br>
                  var future1 = executor.submit(() ->
          fetchURL(url1));<br>
                  var future2 = executor.submit(() ->
          fetchURL(url2));<br>
                  response.send(future1.get() + future2.get());<br>
          } catch (ExecutionException | InterruptedException e) {<br>
                  response.fail(e);<br>
          }<br>
          <br>
        </div>
        <div>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. </div>
        <div><br>
        </div>
        <div>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.</div>
        <div><br>
        </div>
        <div>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.</div>
      </div>
    </blockquote>
    <pre cols="72">-- 
Cheers,



Viktor Klang
Software Architect, Java Platform Group
Oracle</pre>
  </div>

</blockquote></div>