#java #multithreading #concurrency
Вопрос:
Мне нужно одновременно вызвать несколько (менее 30) API-интерфейсов REST и немедленно вернуть первый ответ, который я получу от этих вызовов API.
Я реализовал такой метод, как этот:
public String fetchFastest(List<URL> urls) throws ExecutionException, InterruptedException {
var executor = Executors.newFixedThreadPool(urls.size());
var futures = new ArrayList<Future<String>>();
urls.forEach(url -> {
futures.add(
executor.submit(() -> getResponse(url)) // performs a http request with timeout and returns the response (or null in the case of failure)
);
});
var failed = new ArrayList<Future<String>>();
while (failed.size() < futures.size()) {
for(var f: futures) {
if(f.isDone() amp;amp; !failed.contains(f)) {
if(f.get() == null) {
failed.add(f);
}
else {
executor.shutdownNow();
return f.get();
}
}
}
}
throw new RuntimeException("No response.");
}
Этот подход, похоже, работает. Однако, учитывая, что этот метод вызывается часто, и тот факт, что создание пулов потоков является дорогостоящей задачей (и я создаю их с каждым вызовом метода), я задаюсь вопросом, могу ли я как-то улучшить этот подход или найти другие решения, которые быстрее и имеют меньшие вычислительные затраты.
Любые предложения приветствуются. Заранее спасибо.
Комментарии:
1. Сначала я бы вынул пул потоков и создал общий, вместо того чтобы каждый раз создавать новый. Вам также необходимо добавить инструменты для измерения производительности, чтобы вы могли определить, эффективны ли вносимые вами изменения.
2. Будет ли этот метод вызываться одновременно?
Ответ №1:
Прежде всего, используйте executor.invokeAny()
. Он будет ждать, пока одна из задач не будет успешно выполнена, и отменит остальные (так что заменит почти весь ваш код выше).
О создании пула потоков при каждом вызове метода — иногда это имеет смысл. Но в начале я бы начал с общего пула ( ForkJoinPool.commonPool()
) или создал свой собственный статический (==многоразовый) пул и заменил его только вызовом пула для каждого метода после анализа производительности.
Ответ №2:
Оптимизация 1. Создайте статический пул Fixedthread.
Вы можете определить размер пула потоков по: (Количество одновременных вызовов метода fetchFastest) x (urls.size())
Это лучше, так как GC не будет очищать экземпляры пула потоков, которые вы создавали при каждом вызове функции, и статический пул Fixedthread будет использоваться повторно.
Оптимизация 2: Прерывание потоков, которые больше не нужны.
Прервите все остальные потоки, как только вы получите первый ответ. Это позволит сэкономить циклы процессора при напряженном ожидании и вычислениях(если таковые имеются).