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.

2
  • 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 CompletableFutures and wait for those.
    – Kayaman
    Commented Apr 3, 2020 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
    Commented Apr 3, 2020 at 20:55

1 Answer 1

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");
4
  • 4
    Replace your iterator stuff with CompletableFuture.allOf(futures).join();. No need to spin the main thread or over-engineer an iterator.
    – Kayaman
    Commented Apr 3, 2020 at 21:03
  • Thank you Kayaman, it's a good hint. Multithreading is my weakness. Commented Apr 3, 2020 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.
    – Kayaman
    Commented Apr 3, 2020 at 21:09
  • @Kayaman What you mean the iterator? Iterator<CompletableFuture<Void>> iterator = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]).join(); ?
    – janusz j
    Commented Apr 3, 2020 at 21:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.