2

I have a method name someTask that I have to invoke 100 times and I am using asynchronous coding as below.

for (int i = 0; i < 100; i++) { 
    futures.add(CompletableFuture.supplyAsync(  
       () -> { someTask(); }, 
       myexecutor
    ));
}

If an exception occurs on any thread while executing someTask() I want to interrupt all the current threads and also stop future threads from executing. What is the best way to handle this?

Update: I am using ThreadPoolTaskExecutor.

want2learn
  • 2,471
  • 2
  • 20
  • 37

2 Answers2

0

Since you don't seem to be using the CompletableFuture specific methods, you could use an ExecutorService directly:

for (int i = 0; i < 100; i++) {
  executor.submit(() -> {
      try {
        someTask();
      } catch (InterruptedException e) {
        //interrupted, just exit
        Thread.currentThread().interrupt();
      } catch (Exception e) {
        //some other exception: cancel everything
        executorService.shutdownNow();
      }
    });
}

shutdownNow will interrupt all the already submitted tasks and the executor will refuse new task submission with a RejectedExecutionException.

The main drawback with this approach is that the ExecutorService cannot be reused, although you can of course create a new one. If that's a problem you will need another way.

Note that for this to work efficiently someTask() needs to regularly check the interrupted status of the thread and exit on interruption, or use interruptible methods. For example if someTask is a loop running some calculations and you never check the Thread interrupted status, the loop will run entirely and will not be stopped.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • I am using `ThreadPoolTaskExecutor` to create myexecutor. Can you please elaborate what you mean my " ExecutorService cannot be reused" – want2learn Jun 29 '20 at 21:56
  • 1
    @want2learn It means what he has written: *"[...] the executor will refuse new task submission with a `RejectedExecutionException`."*. The executor will be "shutdown", i.e. it won't accept new tasks anymore, therefore it cannot be reused. You'll have to create a new executor if you want to use one. – akuzminykh Jun 29 '20 at 22:03
  • But I am using `ThreadPoolTaskExecutor` and not `ExecutorService`. – want2learn Jun 29 '20 at 22:08
  • @want2learn I don't know that class, is it part of Spring? If it is you may want to tag your question with Spring as the answer will probably different than using standard Java. – assylias Jun 29 '20 at 22:10
  • 2
    @want2learn since `ThreadPoolTaskExecutor` has the method `getThreadPoolExecutor()`, to get the underlying `ThreadPoolExecutor` (an implementation of `ExecutorService`), you can do everything this answer describes. – Holger Jun 30 '20 at 10:28
  • @Holger I wasn't sure if the executor lifecycle is supposed to be managed by the container and whether there are potential side effects if you shut it down. – assylias Jun 30 '20 at 10:35
  • 1
    It has a `shutdown()` method on its own, which by default has a `shutdownNow()` like behavior of interrupting all tasks. Unless [this flag](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.html#setWaitForTasksToCompleteOnShutdown-boolean-) has been set. – Holger Jun 30 '20 at 12:54
0

There are many ways to do that and all depend on the exact use case. But all ways (I'd say) have one thing in common: you'll have to end the tasks in a controlled manner. You can't just "instant-terminate" the execution of a task because this could lead to inconsitent data and other problems. This is usually done by checking some flag, e.g. Thread.isInterrupted(), and ending the execution manually if it's signaling that the execution shall cancel. You can use a custom flag as well, which I'll show:

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

class Main  {
    
    static final ExecutorService executor = Executors.newCachedThreadPool();
    
    
    // just dummy stuff to simulate calculation
    static void someStuff(AtomicBoolean cancel) {
        if (cancel.get()) {
            System.out.println("interrupted");
            return;
        }
        System.out.println("doing stuff");
        for (int i = 0; i < 100_000_000; i++) {
            Math.sqrt(Math.log(i));
        }
    }
    
    
    static void someTask(AtomicBoolean cancel) {
        someStuff(cancel);
        someStuff(cancel);
        someStuff(cancel);
    }
    
    
    public static void main(String[] args) {
        final Set<CompletableFuture<?>> futures = new HashSet<>();
        final AtomicBoolean cancel = new AtomicBoolean();
        
        for (int i = 0; i < 10; i++) { 
            futures.add(CompletableFuture.supplyAsync(  
               () -> {someTask(cancel); return null;}, executor
            ));
        }
        futures.add(CompletableFuture.supplyAsync(() -> {
            try {
                throw new RuntimeException("dummy exception");
            }
            catch (RuntimeException re) {
                cancel.set(true);
            }
            return null;
        }));
        
        futures.forEach(cf -> cf.join());
        executor.shutdownNow();
    }
}

Notice that the final task is throwing an exception and handles it by setting the flag cancel to true. All the other tasks will see that by checking it periodically. They will cancel their execution if the flag signals it. If you comment out the exception-throwing task, all tasks will just finish normally.

Note that this approach is independent of the executor used.

akuzminykh
  • 4,522
  • 4
  • 15
  • 36
  • sorry but i couldn't understand what you are trying to demonstrate here. Sorry I am new to concurrency world. – want2learn Aug 28 '20 at 03:11
  • @want2learn That's fine. :-) The question is: "Interrupt all threads if an exception occurs in any?" This is what I'm showing here: The class `CompletableFuture` is a nice tool to fast create async-running code. Check out [this](https://www.baeldung.com/java-completablefuture) for details. Im creating a bunch of async-running tasks using the `someTask`-function when I create each CF. Notice that there's an `AtomicBoolean` passesd, which is shared by all tasks. The bool acts as a flag to communicate between the tasks. If it is set to *true*, this is an indicator for task to stop running ... – akuzminykh Aug 28 '20 at 07:47
  • ... Notice that `someStuff` returns whenever `cancel` is `true`, which is the signal I've mentioned. Now check out the `main`-function again: There is one more task that does nothing but throwing an exception. The exception is catched within the same task and `cancel` is set to be `true`. All the other running tasks will see that and stop running as they all return out of their current running function. So what happened is: One thread threw an exception and all other threads stopped their execution because of that. – akuzminykh Aug 28 '20 at 07:49