I have this piece of code in Java and I tried it out on Java 21 (Eclipse Temurin and GraalVM)
public static void main(String[] args) {
Thread.currentThread().interrupt();
long start = System.currentTimeMillis();
System.out.println("started measuring ...");
int i = 0;
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<?>> futures = new ArrayList<>();
futures.add(executor.submit(() -> "api1"));
futures.add(executor.submit(() -> "api2"));
futures.add(executor.submit(() -> "api3"));
for (; i < futures.size(); i++) {
System.out.println((i + 1) + " " + futures.get(i).get());
}
} catch (ExecutionException e) {
System.out.println("error: " + e.getCause());
throw new RuntimeException(e);
} catch (InterruptedException e) {
System.out.println("interrupted after " + (System.currentTimeMillis() - start) + " at " + (i + 1));
throw new RuntimeException(e);
}
}
Sometimes the main thread does not get interrupted, sometimes it does. So the output is either:
started measuring ...
interrupted after 0 at 1
Exception in thread "main" java.lang.RuntimeException: java.lang.InterruptedException
at rs.sf.App.main(App.java:70)
Caused by: java.lang.InterruptedException
at java.base/java.util.concurrent.FutureTask.awaitDone(FutureTask.java:471)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:190)
at rs.sf.App.main(App.java:63)
Or:
started measuring ...
1 api1
2 api2
3 api3
I couldn't find what's the reason for this non-deterministic behavior. I thought that the first statement (the main thread setting the interrupt flag on itself) is executed before any other subsequent line of code and should therefore the execution should always result in InterruptedException
EDIT:
If I write futures with a Thread.sleep, the behavior becomes deterministic and the InterruptedException is always thrown, i.e. like this:
futures.add(executor.submit(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "api1";
}));
FutureTask.get(...)does not check the interrupt flag if there already is a result available (the fact of wether a result is available depends on how the JVM and OS schedules the main thread and the carrier threads, and is not guranteed to follow any rules whatsoever)Future::get()says it throws anInterruptedException"if the current thread was interrupted while waiting". If all the futures are complete by the time you callgeton any of them, then it's legal for them to return the result instead of throwing an interrupted exception.