Получите результат из будущего, не выполнив все - PullRequest
0 голосов
/ 26 мая 2018

Я создаю 10 потоков и добавляю к ним 2 вида работ следующим образом.

public class ParallelAdder {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService executor = Executors.newFixedThreadPool(10);
            List<Future<Integer>> list = new ArrayList<Future<Integer>>();
            for (int i = 0; i < 10; i++) {
                Future<Integer> future;
                if (i % 2 == 0) {
                    future = executor.submit(new Call1());
                } else {
                    future = executor.submit(new Call2());
                }

                list.add(future);

            }

            for(Future<Integer> fut : list) {
                System.out.println("From future is "+fut.get());
            }
        }
    }

class Call1 implements Callable<Integer> {
    public Integer call() throws Exception {
        System.out.println("Calling 1");
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 10;
    }

}

class Call2 implements Callable<Integer> {
    public Integer call() throws Exception {
        System.out.println("Calling 2");
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 20;
    }

}

Задание Call2 возвращается быстрее по сравнению с Call1.В моем будущем списке я хочу получить результат работы, как только он завершится.Это не должно зависеть от выполнения всех других заданий.
Здесь возврат Call2 ожидает Call1.Как это решить?

1 Ответ

0 голосов
/ 26 мая 2018

Проблема в том, что вы ждете, вызывая блокировку get здесь:

for(Future<Integer> fut : list) {
    System.out.println("From future is "+fut.get());
}

Чтобы решить эту проблему, вам нужно использовать реактивный код.Вы можете использовать завершаемый API будущего, который предназначен для декларативного реактивного API, подобного Future:

ExecutorService executor = Executors.newFixedThreadPool(10);

Supplier<Integer> call1Supplier = () -> {
    System.out.println("Calling 1");
    try {
        Thread.sleep(100000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 10;
};

Supplier<Integer> call2Supplier = () -> {
    System.out.println("Calling 1");
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return 10;
};

Затем его можно отправить в ту же службу исполнителя, но с использованием реактивного CompletableFuture, который поддерживаетобъекты, похожие на обратные вызовы.

List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) {
        CompletableFuture<Void> future = 
                CompletableFuture.supplyAsync(call1Supplier, executor)
                .thenAccept(number -> System.out.println("From future is " + number));
        futures.add(future);
    } else {
        CompletableFuture<Void> future = 
                CompletableFuture.supplyAsync(call2Supplier, executor)
                .thenAccept(number -> System.out.println("From future is " + number));
        futures.add(future);
    }
}

Следующее просто для того, чтобы убедиться, что текущий поток не завершает работу до завершения всех асинхронных задач.Но если это долго работающее приложение, такое как сервер, это может быть ненужным

for (CompletableFuture<Void> future : futures) {
    future.join();
}

Обратите внимание, что я выделил код из Call1 и Call2, поскольку реализация Callable не являетсянеобходимо.Но все же хорошая идея - хранить его в отдельном классе (если только объекты функций не в порядке).

...