Introducing ShutdownOnSuccess – Concurrency – Virtual Threads, Structured Concurrency

221. Introducing ShutdownOnSuccess

In the previous problem, we introduced StructuredTaskScope and use it to solve a task via a single virtual thread (a single Subtask). Basically, we fetched the tester with id 1 from our server (we had to wait until this one was available). Next, let’s assume that we still need a single tester, but not mandatory the one with id 1. This time, it could be any of ids 1, 2, or 3. We simply take the first one that is available from these three, and we cancel the other two requests.Especially for such scenarios, we have an extension of StructuredTaskScope called StructuredTaskScope.ShutdownOnSuccess. This scope is capable to return the result of the first task that completes successfully and interrupts the rest of the threads. It follows the “invoke any” model and it can be used as follows:

public static TestingTeam buildTestingTeam()
       throws InterruptedException, ExecutionException {
      
  try (ShutdownOnSuccess scope
      = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
          
    Subtask<String> subtask1
      = scope.fork(() -> fetchTester(1));
    Subtask<String> subtask2
      = scope.fork(() -> fetchTester(2));
    Subtask<String> subtask3
      = scope.fork(() -> fetchTester(3));
                                               
    scope.join();
          
    logger.info(() -> “Subtask-1 state: ” + future1.state());
    logger.info(() -> “Subtask-2 state: ” + future2.state());
    logger.info(() -> “Subtask-3 state: ” + future3.state());
    String result = (String) scope.result();
    logger.info(result);
          
    return new TestingTeam(result);
  }
}

Here, we fork three subtasks (threads) that will compete with each other to complete. The first subtask (thread) that completes successfully wins and returns. The result() method returns this result (if none of the subtasks (threads) complete successfully then it throws ExecutionException).If we check the state of these three Subtask we can see that one succeeds while the other two are unavailable:

[09:01:50] Subtask-1 state: UNAVAILABLE
[09:01:50] Subtask-2 state: SUCCESS
[09:01:50] Subtask-3 state: UNAVAILABLE

Of course, you don’t need the code that checks/print the state of each Subtask. It was added here just to highlight how ShutdownOnSuccess works. You don’t even need the explicit Subtask objects since we don’t call get() or something else from this API. Basically, we can resume the code to the following:

public static TestingTeam buildTestingTeam()
       throws InterruptedException, ExecutionException {
  try (ShutdownOnSuccess scope
      = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
    scope.fork(() -> fetchTester(1));
    scope.fork(() -> fetchTester(2));
    scope.fork(() -> fetchTester(3));
    scope.join();
     
    return new TestingTeam((String) scope.result());
  }
}

Done! You just create the scope, fork your subtasks, call join() , and collect the result. So, the scope is really business-focused.A task that completes exceptionally under the ShutdownOnSuccess umbrella will never be chosen to produce a result. However, if all tasks complete exceptionally then we will get an ExecutionException that wraps the exception (the cause) of the first completed task.