I have a for loop which I am trying to parallelize using CompletableFuture.
for (int i = 0; i < 10000; i++) {
doSomething();
doSomethingElse();
}
What I have till now is:
for (int i = 0; i < 10000; i++) {
CompletableFuture.runAsync(() -> doSomething());
CompletableFuture.runAsync(() -> doSomethingElse());
}
I guess this serves the purpose but there is a requirement to print log just before the start and end of all the processing. If I do this:
log("Started doing things");
for (int i = 0; i < 10000; i++) {
CompletableFuture.runAsync(() -> doSomething());
CompletableFuture.runAsync(() -> doSomethingElse());
}
log("Ended doing things");
Does this guarantee that the second log statement will be printed once all the for loop is over since that is executing in a separate thread? If not, is there a way to do this without blocking the main thread?
You have to collect all CompletableFutures and wait for their complete:
log("Started doing things");
List<CompletableFuture> futures = new ArrayList();
for (int i = 0; i < 10000; i++) {
futures.add(CompletableFuture.runAsync(() -> doSomething()));
futures.add(CompletableFuture.runAsync(() -> doSomethingElse()));
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRunAsync(() -> log("Ended doing things"));
Or when you use the ExecutorService:
CompletableFuture.runAsync(() -> {
try {
executorService.invokeAll(tasks);
} catch (InterruptedException) {
e.printStackTrace();
}
log("Ended doing things");
});
I suppose CompletableFuture
is the wrong concept for your needs. If you want to execute an arbitrary number of similar tasks in parallel, the easiest thing is to use the method invokeAll(...)
on an ExecutionService
:
// First, create a list with all tasks you want to execute in parallel
List<Callable<?>> tasks = new ArrayList<>(10000);
for (int i = 0; i < 10000; ++i) {
// we need to create Callables, so if your doSomething method returns void, we have to convert it to a Callable using helper method from class Executors
tasks.add(Executors.callable(this::doSomething));
}
// Then, create an executor service that can execute these tasks
// There are different executors you can choose from, I take one that has a fixed pool of threads
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Last but not least, call invokeAll to execute all tasks and wait for them to complete
executorService.invokeAll(tasks);
// This method will be called when all tasks have been completed successfully:
System.out.println("done");
The trick here is to use the CompletableFuture.allOf
. Try this out.
public static void main(String[] args) {
System.out.println("Started doing things");
List<CompletableFuture<Void>> cfList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CompletableFuture<Void> cfOne = CompletableFuture.runAsync(() -> doSomething());
CompletableFuture<Void> cfTwo = CompletableFuture.runAsync(() -> doSomethingElse());
cfList.addAll(Arrays.asList(cfOne, cfTwo));
}
CompletableFuture<Void> allCfs = CompletableFuture.allOf(cfList.toArray(new CompletableFuture<?>[0]));
allCfs.thenRunAsync(() -> System.out.println("Ended doing things"));
}
This implementation will NOT keep your main thread waiting till you complete all the submitted tasks.
来源:https://stackoverflow.com/questions/53360559/execute-a-for-loop-in-parallel-using-completablefuture-in-java-and-log-the-execu