The executor is closed explicitly rather than with try-with-resources, and then reused. What does this print?
```java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
public class Main {
public static void main(String[] args) {
ExecutorService ex = Executors.newVirtualThreadPerTaskExecutor();
ex.submit(() -> System.out.print("task "));
ex.close();
try {
ex.submit(() -> System.out.print("late "));
System.out.print("accepted ");
} catch (RejectedExecutionException e) {
System.out.print("rejected ");
}
System.out.print(ex.isShutdown() + ":" + ex.isTerminated());
}
}
```
A. task rejected false:false
Accepts that submission is refused but assumes the executor is not formally shut down; rejection is precisely the consequence of isShutdown() being true, so both flags are true.
B. task rejected true:trueCorrect answer
Correct: close() performs an orderly shutdown then awaits termination, so the first task runs, the second submit is rejected, and both isShutdown() and isTerminated() are already true.
C. task accepted false:false
Assumes close() is a no-op on a thread-per-task executor because there is no pool to tear down; the executor still has state and stops accepting work, so the resubmit is rejected.
D. task rejected true:false
Assumes isTerminated() flips only after an explicit awaitTermination call; close() already performs that wait, so termination has been observed by the time it returns.
Explanation
Trace: `close()` on an `ExecutorService` is defined as an orderly `shutdown()` followed by an unbounded wait for termination. So (1) the first task is guaranteed to run and print `task ` before `close()` returns — no `join()` and no `Future.get()` needed; (2) the executor is now shut down, so the second `submit()` is rejected with `RejectedExecutionException` and `rejected ` prints; (3) because `close()` already waited, both `isShutdown()` and `isTerminated()` are `true`. Output: `task rejected true:true`.
Why the others are wrong:
`task rejected true:false` encodes the belief that `isTerminated()` only flips after you explicitly call `awaitTermination(...)`. `close()` does that wait for you, so termination has already been observed by the time it returns.
`task accepted false:false` encodes the belief that `close()` is a no-op on a thread-per-task executor because there is no pool to tear down. There is still executor state: it stops accepting work.
`task rejected false:false` splits the difference — it accepts that submission is refused but assumes the executor is not formally "shut down". Rejection is precisely the consequence of `isShutdown()` being `true`.
Exam tip: `ExecutorService` is `AutoCloseable` (since Java 19) and `close()` = `shutdown()` + await termination. A closed executor is dead, not paused — re-submitting throws `RejectedExecutionException`, it does not silently drop the task. The reverse trap is a stem that submits work, never joins, and expects nothing to print: inside try-with-resources, the implicit `close()` at the end of the block IS the join.