Выполните цикл for параллельно с использованием CompletableFuture в Java и зарегистрируйте выполнение - PullRequest
0 голосов
/ 18 ноября 2018

У меня есть цикл for, который я пытаюсь распараллелить, используя CompletableFuture.

for (int i = 0; i < 10000; i++) {
    doSomething();
    doSomethingElse();
}

Что у меня есть до сих пор:

for (int i = 0; i < 10000; i++) {
    CompletableFuture.runAsync(() -> doSomething());
    CompletableFuture.runAsync(() -> doSomethingElse());
}

Полагаю, это служит цели, но существует требование распечатать журнал непосредственно перед началом и окончанием всей обработки. Если я сделаю это:

log("Started doing things");
for (int i = 0; i < 10000; i++) {
    CompletableFuture.runAsync(() -> doSomething());
    CompletableFuture.runAsync(() -> doSomethingElse());
}
log("Ended doing things");

Гарантирует ли это, что второй оператор журнала будет напечатан, когда закончится весь цикл for, поскольку он выполняется в отдельном потоке? Если нет, есть ли способ сделать это без блокировки основного потока?

Ответы [ 3 ]

0 голосов
/ 18 ноября 2018

Полагаю, CompletableFuture - неправильная концепция для ваших нужд. Если вы хотите параллельно выполнить произвольное количество похожих задач, проще всего использовать метод invokeAll(...) для 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");
0 голосов
/ 18 ноября 2018

Хитрость здесь в том, чтобы использовать CompletableFuture.allOf.Попробуйте это.

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"));
}

Эта реализация НЕ заставит ваш основной поток ждать, пока вы не выполните все представленные задачи.

0 голосов
/ 18 ноября 2018

Вы должны собрать все CompletableFutures и дождаться их завершения:

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"));

Или когда вы используете ExecutorService:

CompletableFuture.runAsync(() -> {
    try {
        executorService.invokeAll(tasks);
    } catch (InterruptedException) {
        e.printStackTrace();
    }
    log("Ended doing things");
});
...