[英]Collecting results from a list of Futures in java
I'm trying to use futures to make concurrent api calls.我正在尝试使用期货进行并发 api 调用。 Code:
代码:
private void init() throws ExecutionException, InterruptedException {
Long start = System.currentTimeMillis();
List<ApiResponse> responses = fetchAllUsingFuture(ids, 3);
log.info(responses.toString());
Long finish = System.currentTimeMillis();
log.info(MessageFormat.format("Process duration: {0} in ms", finish-start));
}
private List<ApiResponse> fetchAllUsingFuture(List<String> ids, int threadCount) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
List<List<String>> chunks = Utils.splitToChunks(ids, threadCount);
List<Future<List<ApiResponse>>> futures = new ArrayList<>();
chunks.forEach(chunk -> {
futures.add(wrapFetchInFuture(chunk));
});
Future<List<ApiResponse>> resultFuture = executorService.submit(() -> {
List<ApiResponse> responses = new ArrayList<>();
futures.forEach(future -> {
try {
responses.addAll(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
});
return responses;
});
executorService.shutdown();
return resultFuture.get();
}
private Future<List<ApiResponse>> wrapFetchInFuture(List<String> ids) {
return new FutureTask<>(() -> {
List<ApiResponse> responses = new ArrayList<>();
ids.forEach(id -> {
responses.add(fetchData(id));
});
return responses;
});
}
private ApiResponse fetchData(String id) {
ResponseEntity<ApiResponse> response = restTemplate.getForEntity(id, ApiResponse.class);
log.info(MessageFormat.format("Fetching from {0}", id));
ApiResponse body = response.getBody();
log.info(MessageFormat.format("Retrieved {0}", body));
return body;
}
It doesn't execute, the app starts and then just pends.它不执行,应用程序启动,然后只是挂起。 Futures don't get fulfilled.
期货没有得到履行。 All advices are appreciated.
所有建议表示赞赏。 PS I'm aware this is much more easily done using CompletableFuture, I was just wondering how to do this with Futures
PS我知道使用CompletableFuture更容易做到这一点,我只是想知道如何使用Futures做到这一点
In the original version of the question, you are creating a list of FutureTasks
but never send them to the ExecutorService
to run them.在问题的原始版本中,您正在创建一个
FutureTasks
列表,但从未将它们发送到ExecutorService
来运行它们。 The tasks never complete, so Future.get
blocks forever.任务永远不会完成,所以
Future.get
永远阻塞。
In the updated version of the question, you have put the code that does the waiting into the executor service as a task.在问题的更新版本中,您已将执行等待的代码作为任务放入执行程序服务中。 The FutureTasks never run, so
FutureTask.get
will still block forever. FutureTasks 永远不会运行,所以
FutureTask.get
仍然会永远阻塞。
I would suggest you change the code in fetchAllUsingFuture
to:我建议您将
fetchAllUsingFuture
中的代码更改为:
List<Callable<List<ApiResponse>>> tasks = new ArrayList<>();
chunks.forEach(chunk -> {
tasks.add(wrapFetchInCallable(chunk));
});
List<Future<List<ApiResponse>>> futures = executorService.invokeAll(tasks);
where wrapFetchInCallable
creates a Callable
instead of FutureTask
:其中
wrapFetchInCallable
创建一个Callable
而不是FutureTask
:
private static Callable<List<ApiResponse>> wrapFetchInCallable(List<String> ids) {
return () -> {
List<ApiResponse> responses = new ArrayList<>();
ids.forEach(id -> {
responses.add(fetchData(id));
});
return responses;
};
}
It looks like you are creating a list of FutureTasks but never send them to the ExecutorService to run them.看起来您正在创建一个 FutureTasks 列表,但从未将它们发送到 ExecutorService 来运行它们。 I have implemented ExecutorService with Future Object as below, i hope it helps you:
我已经用 Future Object 实现了 ExecutorService,如下所示,希望对您有所帮助:
Service layer:服务层:
public List<MovieDTO> searchMoviesParallel(String limit, String offset, String searchPhrase) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<List<MovieDTO>> digitoonResult = executor.submit(new DigitoonSearchTask(limit, offset, searchPhrase));
List<MovieDTO> movieDTOList = digitoonResult.get();
executor.shutdown();
return movieDTOList;
}
And my Search task(DigitoonSearchTask class) is as below:我的搜索任务(DigitoonSearchTask 类)如下:
public class DigitoonSearchTask implements Callable<List<MovieDTO>> {
private String limit;
private String offset;
private String searchPhrase;
private final static String digitoonSearchBaseUrl = "http://apitwo.xxx.com/partner/search/?q=";
public DigitoonSearchTask(String limit, String offset, String searchPhrase) {
this.limit = limit;
this.offset = offset;
this.searchPhrase = searchPhrase;
}
@Override
public List<MovieDTO> call() throws Exception {
List<MovieDTO> movieDTOList = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
try {
String uri = digitoonSearchBaseUrl + URLEncoder.encode(searchPhrase, "utf-8") + "&limit=" + limit + "&offset=" + offset;
URL url = new URL(uri);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("authorization", "xxxxxxxxxx");
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ conn.getResponseCode());
}
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
String output;
while ((output = br.readLine()) != null) {
movieDTOList = Arrays.asList(mapper.readValue(output, MovieDTO[].class));
}
br.close();
conn.disconnect();
} catch (UnknownHostException e) {
call();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return movieDTOList;
}}
consider that now I have just one API and after getting others they can be added as another Search task in service layer by increasing the thread number.考虑到现在我只有一个 API 并且在获得其他之后,可以通过增加线程数将它们添加为服务层中的另一个搜索任务。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.