在花了一天学习java Concurrency API之后,我仍然不知道如何在CompletableFuture和ExecutorService类的帮助下创建以下功能:
当我在我的REST端点上收到请求时,我需要:
我已经为这些编写了构建块(方法如:getMatchingObjectsFromDB(callerPayload),getURLs(resultOfgetMachingObjects),sendHttpRequest(url,methodType)等等)),我只是无法弄清楚如何绑定第1步和第3步一起。我会在步骤1中使用CompletableFuture.supplyAsync()
,然后我需要CompletableFuture.thenComponse
方法来启动第3步,但我不清楚如何使用此API进行并行操作。但是ExecutorService executor = Executors.newWorkStealingPool();
非常直观,它根据可用的处理能力来创建一个线程池,并且可以通过invokeAll()
方法提交任务。
如何一起使用CompletableFuture
和ExecutorService
?或者我如何保证CompletableFuture
并行执行任务列表?展示代码片段将非常感激。感谢。
答案 0 :(得分:2)
您应该使用join()
等待所有线程完成。
创建Map<String, Boolean> result
以存储您的请求结果。
在您的控制器中:
public void yourControllerMethod() {
CompletableFuture.runAsync(() -> yourServiceMethod());
}
在您的服务中:
// Execute your logic to get List<String> urls
List<CompletableFuture> futures = urls.stream().map(v ->
CompletableFuture.supplyAsync(url -> requestUrl(url))
.thenAcceptAsync(requestResult -> result.put(url, true or false))
).collect(toList()); // You have list of completeable future here
然后使用.join()
等待所有线程(请记住,您的服务已在其自己的线程中执行)
CompletableFuture.allOf(futures).join();
然后,您可以通过访问result
地图
修改强>
请发布您的程序代码,以便其他人也可以理解您。
我已阅读您的代码,以下是必要的修改:
当没有注释掉这个for循环时,接收器webserver得到了 相同的请求两次, 我不明白这个for循环的目的。
对不起我以前的回答,我没有清理它。这只是我头脑中的一个暂时的想法,我忘了在最后删除:D
只需将其从代码中删除
即可// allOf()只接受数组,因此需要转换List / *代码永远不会超过这一部分(我知道allOf()是一个阻塞调用),即使在接收者获得HTTP请求之后很久
with the correct payload. I'm not sure yet where exactly the code gets stuck */
您的地图应该是ConcurrentHashMap
,因为您稍后会同时修改它。
Map<String, Boolean> result = new ConcurrentHashMap<>();
如果您的代码仍未按预期运行,我建议您删除parallelStream()
部分。
CompletableFuture
和parallelStream
使用公共forkjoin池。我认为游泳池已经用尽了。
您应该为CompletableFuture
:
Executor pool = Executors.newFixedThreadPool(10);
使用该池执行您的请求:
CompletableFuture.supplyAsync(YOURTASK, pool).thenAcceptAsync(Yourtask, pool)
答案 1 :(得分:0)
为了完成,这里是清理和测试后代码的相关部分(感谢MạnhQuyếtNguyễn):
休息控制器类:
@POST
@Path("publish")
public Response publishEvent(PublishEvent eventPublished) {
/*
Payload verification, etc.
*/
//First send the event to the right subscribers, then send the resulting hashmap<String url, Boolean subscriberGotTheRequest> back to the publisher
CompletableFuture.supplyAsync(() -> EventHandlerService.propagateEvent(eventPublished)).thenAccept(map -> {
if (eventPublished.getDeliveryCompleteUri() != null) {
String callbackUrl = Utility
.getUri(eventPublished.getSource().getAddress(), eventPublished.getSource().getPort(), eventPublished.getDeliveryCompleteUri(), isSecure,
false);
try {
Utility.sendRequest(callbackUrl, "POST", map);
} catch (RuntimeException e) {
log.error("Callback after event publishing failed at: " + callbackUrl);
e.printStackTrace();
}
}
});
//return OK while the event publishing happens in async
return Response.status(Status.OK).build();
}
服务类:
private static List<EventFilter> getMatchingEventFilters(PublishEvent pe) {
//query the database, filter the results based on the method argument
}
private static boolean sendRequest(String url, Event event) {
//send the HTTP request to the given URL, with the given Event payload, return true if the response is positive (status code starts with 2), false otherwise
}
static Map<String, Boolean> propagateEvent(PublishEvent eventPublished) {
// Get the event relevant filters from the DB
List<EventFilter> filters = getMatchingEventFilters(eventPublished);
// Create the URLs from the filters
List<String> urls = new ArrayList<>();
for (EventFilter filter : filters) {
String url;
try {
boolean isSecure = filter.getConsumer().getAuthenticationInfo() != null;
url = Utility.getUri(filter.getConsumer().getAddress(), filter.getPort(), filter.getNotifyUri(), isSecure, false);
} catch (ArrowheadException | NullPointerException e) {
e.printStackTrace();
continue;
}
urls.add(url);
}
Map<String, Boolean> result = new ConcurrentHashMap<>();
Stream<CompletableFuture> stream = urls.stream().map(url -> CompletableFuture.supplyAsync(() -> sendRequest(url, eventPublished.getEvent()))
.thenAcceptAsync(published -> result.put(url, published)));
CompletableFuture.allOf(stream.toArray(CompletableFuture[]::new)).join();
log.info("Event published to " + urls.size() + " subscribers.");
return result;
}
调试这比平常有点困难,有时代码只是神奇地停止了。为了解决这个问题,我只将代码部分放入异步任务中,这是绝对必要的,我确保任务中的代码使用了线程安全的东西。我起初也是一个愚蠢的人,我EventHandlerService.class
内的方法使用了synchronized
关键字,导致Service class方法中的CompletableFuture没有执行,因为它默认使用一个线程池。
标记为synchronized的逻辑变为同步块,在任何给定时间只允许一个线程执行。