В целом подход не является неправильным, но есть вещи, которые нужно улучшить.
В частности, вы не должны использовать необработанные типы , например, CompletableFuture
.
Когда populateSection…
возвращает PdfPTable
, вы должны последовательно использовать CompletableFuture<PdfPTable>
во всем коде.
* 1013 Т.е. *
CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> populateSection1(arguments));
CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> populateSection2(arguments));
...
CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> populateSection10(arguments));
даже если эти методы не объявляют тип возвращаемого значения, которое, как вы предполагаете, всегда возвращаются во время выполнения, вы должны вставить приведение типа на этом раннем этапе:
CompletableFuture<PdfPTable> section1Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection1(arguments));
CompletableFuture<PdfPTable> section2Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection2(arguments));
...
CompletableFuture<PdfPTable> section10Future = CompletableFuture.supplyAsync(() -> (PdfPTable)populateSection10(arguments));
Затем вы можете использовать
Stream.of(section1Future, section2Future, ..., section10Future)
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.forEachOrdered(el -> populatePdfElement(document, el));
Не используя необработанные типы, вы уже получаете желаемый тип результата и можете выполнять операции 3-го шага, то есть фильтрацию и выполнение последнего действия, прямо в этой потоковой операции.
Если вам все еще нужен список, вы можете использовать
List<PdfPTable> results = Stream.of(section1Future, section2Future, ..., section10Future)
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(Collectors.toList());
results.forEach(el -> populatePdfElement(document, el));
Тем не менее, параллелизм зависит от пула потоков, используемого для операции (указано для supplyAsync
). Когда вы не указываете исполнителя, вы получаете пул Fork / Join по умолчанию, используемый параллельными потоками, поэтому в этом конкретном случае вы получите тот же результат, что и
.
List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
() -> populateSection1(arguments),
() -> populateSection2(arguments));
...
() -> populateSection10(arguments)))
.parallel()
.map(Supplier::get)
.filter(Objects::nonNull)
.forEachOrdered(el -> populatePdfElement(document, el));
или
List<PdfPTable> results = Stream.<Supplier<PdfPTable>>.of(
() -> populateSection1(arguments),
() -> populateSection2(arguments));
...
() -> populateSection10(arguments)))
.parallel()
.map(Supplier::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
results.forEach(el -> populatePdfElement(document, el));
Хотя оба варианта гарантируют, что populatePdfElement
будет вызываться в правильном порядке и по одному, только последний будет выполнять все вызовы из инициирующего потока.
Что касается обработки исключений, вы получите любое исключение, выданное поставщиком, заключенное в CompletionException
при вызове CompletableFuture::join
. Цепочка что-то вроде .exceptionally (ex -> { throw new RuntimeException(ex.getCause()); });
не имеет смысла, новый RuntimeException
также будет заключен в CompletionException
при вызове CompletableFuture::join
.
В варианте Stream вы получите исключение без оболочки. Поскольку Supplier
не допускает проверенные исключения, возможны только подтипы RuntimeException
или Error
.
Другие вопросы слишком широки для вопросов и ответов.