2

I have Service.class with start() method:

public void start()  {  
    for (int i = 0; i < companiesList.size(); i++) {
        asychronous.someAsynchronous(...);
    }
    log.info("Start method has finished");
}

I have Asynchronous.class with someAsynchronous() method:

@Async("threadPoolTaskExecutor")
public CompletableFuture<Void> someAsynchronous(some_parameters) {
    //do some stuff
    return null;
}

The log.info() shows up before someAsynchronous() methods has finished. How to force it to wait for log.info() until someSynchronous() methods in loop will finish? Btw: Asynchronous threads are still running after finishing loop.

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
janusz j
  • 321
  • 3
  • 17
  • Of course it shows up before, that's the whole point of the methods being executed asynchronously. Are you sure the method is supposed to be async at all? But you can still get the `CompletableFuture`s and wait for those. – Kayaman Apr 03 '20 at 20:53
  • @Kayaman They are executed in new seperate threads thats fine, but I want to let them to finish and then let main Thread continue. – janusz j Apr 03 '20 at 20:55

1 Answers1

2

The CompletableFuture<Void> calls are requested to be executed but after all of them start in separate threads, the for-loop finishes and the log is printed even before any of them finished. This is the advantage of the asynchronous processing - you don't care about their results and how much time they took to execute.

To achieve you want, you have to periodically check whether all of them are finished before you proceed to the log outoput.

// Adds executions to the List
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < companiesList.size(); i++) {
    futures.add(asychronous.someAsynchronous(...));
}

// Periodical check
Iterator<CompletableFuture<Void>> iterator = futures.iterator();
while (iterator.hasNext()) {
    CompletableFuture<Void> future = iterator.next(); // get the next one
    if (future.isDone()) {                            // if finished...
        //...                                         // ... do an action
        iterator.remove();                            // ... and remove from the Iterator
    }
    if (!iterator.hasNext()) {                        // if you reach the end
        iterator = futures.iterator();                // ... repeat the remaining Futures
    }
}

log.info("Start method has finished");

Note this method doesn't finish until all of the executions are done.


Edit: Thanks to @Kayaman who suggested using a single method dedicated for that replacing the whole Iterator logics. The futures must be an array:

// Adds executions to the List
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < companiesList.size(); i++) {
    futures.add(asychronous.someAsynchronous(...));
}

// Join the completion of all the threads
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

log.info("Start method has finished");
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • 4
    Replace your iterator stuff with `CompletableFuture.allOf(futures).join();`. No need to spin the main thread or over-engineer an iterator. – Kayaman Apr 03 '20 at 21:03
  • Thank you Kayaman, it's a good hint. Multithreading is my weakness. – Nikolas Charalambidis Apr 03 '20 at 21:06
  • 1
    Yes, `CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]).join();`, it's a varargs parameter. @januszj the iterator can be replaced by that. Or `.forEach()` [if you want](https://stackoverflow.com/questions/30705981/what-is-the-recommended-way-to-wait-till-the-completable-future-threads-finish). – Kayaman Apr 03 '20 at 21:09
  • @Kayaman What you mean the iterator? Iterator> iterator = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]).join(); ? – janusz j Apr 03 '20 at 21:11