Java 如何等待CompletableFuture SupplySync(为循环调用)完成,然后收集结果?

Java 如何等待CompletableFuture SupplySync(为循环调用)完成,然后收集结果?,java,asynchronous,java-8,completable-future,Java,Asynchronous,Java 8,Completable Future,并行执行convertOCRStreamToDTO(..)的逻辑,然后在单个线程执行完成时收集其结果 //Unit of logic I want to make it to run in parallel public PagesDTO convertOCRStreamToDTO(String pageId, Integer pageSequence) throws Exception { LOG.info("Get OCR begin for pageId [{}] thread n

并行执行convertOCRStreamToDTO(..)的逻辑,然后在单个线程执行完成时收集其结果

//Unit of logic I want to make it to run in parallel
public PagesDTO convertOCRStreamToDTO(String pageId, Integer pageSequence) throws Exception {
    LOG.info("Get OCR begin for pageId [{}] thread name {}",pageId, Thread.currentThread().getName());
    OcrContent ocrContent = getOcrContent(pageId);
    OcrDTO ocrData = populateOCRData(ocrContent.getInputStream());
    PagesDTO pageDTO = new PagesDTO(pageId, pageSequence.toString(), ocrData);
    return pageDTO; 
}
List pageDTOList=new ArrayList();
//javadoc:使用所有可用的处理器作为其目标并行级别,创建一个工作线程池。
ExecutorService newWorkStealingPool=Executors.newWorkStealingPool();
即时开始=即时。现在();
List pendingTasks=new ArrayList();
List completedTasks=new ArrayList();
CompletableFuture任务=null;
for(InputPageDTO dcInputPageDTO:dcReqDTO.getPages()){
字符串pageId=dcInputPageDTO.getPageId();
任务=可完成的未来
.SupplySync(()->{
试一试{
返回convertOCRStreamToDTO(pageId,pageSequence.getAndIncrement());
}catch(HttpHostConnectException | ConnectTimeoutException e){
LOG.error(“连接到Redis的pageId[{}]时出错”,pageId,e);
CaptureException e1=新CaptureException(Error.getErrorCodes().get(ErrorCodeConstants.REDIS\u连接\u失败),
“在获取pageId[“+pageId+”]“+e.getMessage(),CaptureErrorComponent.Redis_缓存,e)的OCR时,连接到Redis失败);
例外映射put(pageId,e1);
}捕获(捕获异常e){
LOG.error(“获取pageId[{}]的OCR时文档分类引擎服务出错”,pageId,e);
例外映射put(pageId,e);
}捕获(例外e){
LOG.error(“获取pageId[{}]的OCR内容时出错”,pageId,e);
CaptureException e1=新CaptureException(Error.getErrorCodes().get(ErrorCodeConstants.TECHNICAL_失败),
获取pageId的ocr内容时出错:[“+pageId+”]“+e.getMessage(),CaptureErrorComponent.REDIS_缓存,e);
例外映射put(pageId,e1);
}
返回null;
},newWorkStealingPool);
//收集所有异步任务
添加(任务);
}
//TODO:如何避免发生不必要的循环,而这些循环只是为了等待将来的任务完成???
//TODO:寻找最佳解决方案
而(pendingTasks.size()>0){
用于(可完成的未来任务:挂起任务){
if(futureTask!=null&&futureTask.isDone()){
completedTasks.add(未来任务);
pageDTOList.add(futureTask.get());
}
}
pendingTasks.removeAll(已完成的任务);
}
//在将任何pageId的OCR流转换为DTO-时引发异常cougt
for(InputPageDTO dcInputPageDTO:dcReqDTO.getPages()){
if(例外map.containsKey(dcInputPageDTO.getPageId()){
CaptureException e=exceptionMap.get(dcInputPageDTO.getPageId());
投掷e;
}
}
LOG.info({}页的并行处理时间={}),dcReqDTO.getPages().size(),
org.springframework.util.StringUtils.deleteAny(Duration.between(Instant.now(),start.toString().toLowerCase(),“pt-”);
请查看我的上述代码基础待办事项,我有以下两个问题,我正在寻求有关stackoverflow的建议:

1) 我想避免不必要的循环(发生在上面的while循环中),乐观地等待所有线程完成异步执行,然后从中收集结果的最佳方法是什么???请问有人有什么建议吗

2) ExecutorService实例是在我的服务bean类级别创建的,认为它将被重新用于每个请求,而不是在方法的本地创建它,并最终关闭。我在这里干什么??或者在我的思维过程中有什么修正


只需删除
while
if
即可:

List<PagesDTO> pageDTOList = new ArrayList<>();
//javadoc: Creates a work-stealing thread pool using all available processors as its target parallelism level.
ExecutorService newWorkStealingPool = Executors.newWorkStealingPool(); 
Instant start = Instant.now();
List<CompletableFuture<PagesDTO>> pendingTasks = new ArrayList<>();
List<CompletableFuture<PagesDTO>> completedTasks = new ArrayList<>();
CompletableFuture<<PagesDTO>> task = null;

for (InputPageDTO dcInputPageDTO : dcReqDTO.getPages()) {
    String pageId = dcInputPageDTO.getPageId();
    task = CompletableFuture
            .supplyAsync(() -> {
                try {
                    return convertOCRStreamToDTO(pageId, pageSequence.getAndIncrement());
                } catch (HttpHostConnectException | ConnectTimeoutException e) {
                    LOG.error("Error connecting to Redis for pageId [{}]", pageId, e);
                    CaptureException e1 = new CaptureException(Error.getErrorCodes().get(ErrorCodeConstants.REDIS_CONNECTION_FAILURE),
                            " Connecting to the Redis failed while getting OCR for pageId ["+pageId +"] " + e.getMessage(), CaptureErrorComponent.REDIS_CACHE, e);
                    exceptionMap.put(pageId,e1);
                } catch (CaptureException e) {
                    LOG.error("Error in Document Classification Engine Service while getting OCR for pageId [{}]",pageId,e);
                    exceptionMap.put(pageId,e);
                } catch (Exception e) {
                    LOG.error("Error getting OCR content for the pageId [{}]", pageId,e);
                    CaptureException e1 = new CaptureException(Error.getErrorCodes().get(ErrorCodeConstants.TECHNICAL_FAILURE),
                            "Error while getting ocr content for pageId : ["+pageId +"] " + e.getMessage(), CaptureErrorComponent.REDIS_CACHE, e);
                    exceptionMap.put(pageId,e1);
                }
                return null;
            }, newWorkStealingPool);
    //collect all async tasks
    pendingTasks.add(task);
}

//TODO: How to avoid unnecessary loops which is happening here just for the sake of waiting for the future tasks to complete???
//TODO: Looking for the best solutions
while(pendingTasks.size() > 0) {
    for(CompletableFuture<PagesDTO> futureTask: pendingTasks) {
        if(futureTask != null && futureTask.isDone()){
            completedTasks.add(futureTask);
            pageDTOList.add(futureTask.get());
        }
    }
    pendingTasks.removeAll(completedTasks);
}

//Throw the exception cought while getting converting OCR stream to DTO - for any of the pageId
for(InputPageDTO dcInputPageDTO : dcReqDTO.getPages()) {
    if(exceptionMap.containsKey(dcInputPageDTO.getPageId())) {
        CaptureException e = exceptionMap.get(dcInputPageDTO.getPageId());
        throw e;
    }
}

LOG.info("Parallel processing time taken for {} pages = {}", dcReqDTO.getPages().size(),
        org.springframework.util.StringUtils.deleteAny(Duration.between(Instant.now(), start).toString().toLowerCase(), "pt-"));
for(CompletableFutureTask:pendingTasks){
completedTasks.add(未来任务);
pageDTOList.add(futureTask.get());
}
get()
(以及
join()
)将在返回值之前等待将来完成。此外,不需要测试
null
,因为您的列表永远不会包含任何内容


但是,您可能应该更改处理异常的方式
CompletableFuture
有一个特定的机制,用于在调用
get()
/
join()
时处理和重新调用它们。您可能只想在
CompletionException

中包装选中的异常,在
convertocreamtodto()
中返回
供应商
有什么意义?@didier-l是的,我在
convertocreamtodto()
中没有返回
供应商的意义,我更新了上面的示例代码,请建议如何在while循环中避免不必要的循环??有没有更好的方法可以在线程完成执行后立即收集结果?关于
ExecutorService
的问题,最好作为单独的问题来提问-这可能已经在这里的某个地方得到了回答,所以请先搜索以避免创建重复的问题。你就是那个人!令人惊叹的。这解决了我的疑问。以这种方式处理异常
futureTask.completeeexceptionaly(e1)
我正在探索更多的方法来处理它。你基本上解决了我的问题。@ravibeli:@didier-l For#2我搜索过,所以没有人问同样的问题,我在等待我问题的答案#2@ravibeli-还有其他类似的问题,:-):-)好的,我明白了。我正在使用Dropwizard框架和spring集成,我已经在上下文级别创建了executor服务实例来重用同一个实例,这是正确的方法,正如我在您提供的链接中读到的那样。
for(CompletableFuture<PagesDTO> futureTask: pendingTasks) {
    completedTasks.add(futureTask);
    pageDTOList.add(futureTask.get());
}