From f265b518417056fecca2ca29a0170b0a363e3fa3 Mon Sep 17 00:00:00 2001 From: lucaCambi77 Date: Tue, 6 Jun 2023 03:52:46 +0200 Subject: [PATCH] [BAEL-6486] - Returning a Value After Finishing Thread's Job in Java (#14101) * feat: threads with return value * pmd violation --- .../task/FactorialCalculator.java | 29 +++++++++++++ .../task/callable/CallableExecutor.java | 38 ++++++++++++++++ .../task/callable/CallableFactorialTask.java | 20 +++++++++ .../task/fork/ForkExecutor.java | 34 +++++++++++++++ .../task/fork/ForkFactorialTask.java | 43 +++++++++++++++++++ .../callable/CallableUnitTest.java | 22 ++++++++++ .../CompletableFutureUnitTest.java | 39 +++++++++++++++++ .../threadreturnvalue/fork/ForkUnitTest.java | 27 ++++++++++++ 8 files changed, 252 insertions(+) create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/FactorialCalculator.java create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableExecutor.java create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableFactorialTask.java create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkExecutor.java create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkFactorialTask.java create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/callable/CallableUnitTest.java create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/completableFuture/CompletableFutureUnitTest.java create mode 100644 core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/fork/ForkUnitTest.java diff --git a/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/FactorialCalculator.java b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/FactorialCalculator.java new file mode 100644 index 0000000000..4d0c5ab69d --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/FactorialCalculator.java @@ -0,0 +1,29 @@ +package com.baeldung.concurrent.threadreturnvalue.task; + +import java.math.BigInteger; + +public class FactorialCalculator { + + public static BigInteger factorial(BigInteger end) { + BigInteger start = BigInteger.ONE; + BigInteger res = BigInteger.ONE; + + for (int i = start.add(BigInteger.ONE) + .intValue(); i <= end.intValue(); i++) { + res = res.multiply(BigInteger.valueOf(i)); + } + + return res; + } + + public static BigInteger factorial(BigInteger start, BigInteger end) { + BigInteger res = start; + + for (int i = start.add(BigInteger.ONE) + .intValue(); i <= end.intValue(); i++) { + res = res.multiply(BigInteger.valueOf(i)); + } + + return res; + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableExecutor.java b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableExecutor.java new file mode 100644 index 0000000000..260870d0a7 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableExecutor.java @@ -0,0 +1,38 @@ +package com.baeldung.concurrent.threadreturnvalue.task.callable; + +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class CallableExecutor { + + public BigInteger execute(List tasks) { + + BigInteger result = BigInteger.ZERO; + + ExecutorService cachedPool = Executors.newCachedThreadPool(); + + List> futures; + + try { + futures = cachedPool.invokeAll(tasks); + } catch (InterruptedException e) { + // exception handling example + throw new RuntimeException(e); + } + + for (Future future : futures) { + try { + result = result.add(future.get()); + } catch (InterruptedException | ExecutionException e) { + // exception handling example + throw new RuntimeException(e); + } + } + + return result; + } +} diff --git a/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableFactorialTask.java b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableFactorialTask.java new file mode 100644 index 0000000000..4c38d2fb19 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/callable/CallableFactorialTask.java @@ -0,0 +1,20 @@ +package com.baeldung.concurrent.threadreturnvalue.task.callable; + +import static com.baeldung.concurrent.threadreturnvalue.task.FactorialCalculator.factorial; + +import java.math.BigInteger; +import java.util.concurrent.Callable; + +public class CallableFactorialTask implements Callable { + + private final Integer value; + + public CallableFactorialTask(int value) { + this.value = value; + } + + @Override + public BigInteger call() { + return factorial(BigInteger.valueOf(value)); + } +} diff --git a/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkExecutor.java b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkExecutor.java new file mode 100644 index 0000000000..ed199973ce --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkExecutor.java @@ -0,0 +1,34 @@ +package com.baeldung.concurrent.threadreturnvalue.task.fork; + +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Future; + +public class ForkExecutor { + + private final ForkJoinPool forkJoinPool = ForkJoinPool.commonPool(); + + public BigInteger execute(ForkFactorialTask forkFactorial) { + return forkJoinPool.invoke(forkFactorial); + } + + public BigInteger execute(List> forkFactorials) { + List> futures = forkJoinPool.invokeAll(forkFactorials); + + BigInteger result = BigInteger.ZERO; + + for (Future future : futures) { + try { + result = result.add(future.get()); + } catch (InterruptedException | ExecutionException e) { + // exception handling example + throw new RuntimeException(e); + } + } + + return result; + } +} diff --git a/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkFactorialTask.java b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkFactorialTask.java new file mode 100644 index 0000000000..b45a88655c --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/main/java/com/baeldung/concurrent/threadreturnvalue/task/fork/ForkFactorialTask.java @@ -0,0 +1,43 @@ +package com.baeldung.concurrent.threadreturnvalue.task.fork; + +import static com.baeldung.concurrent.threadreturnvalue.task.FactorialCalculator.factorial; + +import java.math.BigInteger; +import java.util.concurrent.RecursiveTask; + +public class ForkFactorialTask extends RecursiveTask { + + private final int start; + private final int end; + private final int threshold; + + public ForkFactorialTask(int end, int threshold) { + this.start = 1; + this.end = end; + this.threshold = threshold; + } + + public ForkFactorialTask(int start, int end, int threshold) { + this.start = start; + this.end = end; + this.threshold = threshold; + } + + @Override + protected BigInteger compute() { + + BigInteger sum = BigInteger.ONE; + + if (end - start > threshold) { + + int middle = (end + start) / 2; + + return sum.multiply(new ForkFactorialTask(start, middle, threshold).fork() + .join() + .multiply(new ForkFactorialTask(middle + 1, end, threshold).fork() + .join())); + } + + return sum.multiply(factorial(BigInteger.valueOf(start), BigInteger.valueOf(end))); + } +} diff --git a/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/callable/CallableUnitTest.java b/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/callable/CallableUnitTest.java new file mode 100644 index 0000000000..d9271b7603 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/callable/CallableUnitTest.java @@ -0,0 +1,22 @@ +package com.baeldung.concurrent.threadreturnvalue.callable; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigInteger; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import com.baeldung.concurrent.threadreturnvalue.task.callable.CallableExecutor; +import com.baeldung.concurrent.threadreturnvalue.task.callable.CallableFactorialTask; + +public class CallableUnitTest { + + private final CallableExecutor callableExecutor = new CallableExecutor(); + + @Test + void givenCallableExecutor_whenExecuteFactorial_thenResultOk() { + BigInteger result = callableExecutor.execute(Arrays.asList(new CallableFactorialTask(5), new CallableFactorialTask(3))); + assertEquals(BigInteger.valueOf(126), result); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/completableFuture/CompletableFutureUnitTest.java b/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/completableFuture/CompletableFutureUnitTest.java new file mode 100644 index 0000000000..4322aa6847 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/completableFuture/CompletableFutureUnitTest.java @@ -0,0 +1,39 @@ +package com.baeldung.concurrent.threadreturnvalue.completableFuture; + +import static com.baeldung.concurrent.threadreturnvalue.task.FactorialCalculator.factorial; +import static java.util.concurrent.CompletableFuture.allOf; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigInteger; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; + +import org.junit.jupiter.api.Test; + +public class CompletableFutureUnitTest { + + @Test + void givenCompletableFuture_whenSupplyAsyncFactorial_thenResultOk() throws ExecutionException, InterruptedException { + CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> factorial(BigInteger.valueOf(10))); + assertEquals(BigInteger.valueOf(3628800), completableFuture.get()); + } + + @Test + void givenCompletableFuture_whenComposeTasks_thenResultOk() throws ExecutionException, InterruptedException { + CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> factorial(BigInteger.valueOf(3))) + .thenCompose(inputFromFirstTask -> CompletableFuture.supplyAsync(() -> factorial(inputFromFirstTask))); + assertEquals(BigInteger.valueOf(720), completableFuture.get()); + } + + @Test + void givenCompletableFuture_whenAllOfTasks_thenResultOk() { + CompletableFuture asyncTask1 = CompletableFuture.supplyAsync(() -> BigInteger.valueOf(5)); + CompletableFuture asyncTask2 = CompletableFuture.supplyAsync(() -> "3"); + + BigInteger result = allOf(asyncTask1, asyncTask2).thenApplyAsync(fn -> factorial(asyncTask1.join()).add(factorial(new BigInteger(asyncTask2.join()))), Executors.newFixedThreadPool(1)) + .join(); + + assertEquals(BigInteger.valueOf(126), result); + } +} diff --git a/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/fork/ForkUnitTest.java b/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/fork/ForkUnitTest.java new file mode 100644 index 0000000000..5012fc3745 --- /dev/null +++ b/core-java-modules/core-java-concurrency-basic-3/src/test/java/com/baeldung/concurrent/threadreturnvalue/fork/ForkUnitTest.java @@ -0,0 +1,27 @@ +package com.baeldung.concurrent.threadreturnvalue.fork; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigInteger; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import com.baeldung.concurrent.threadreturnvalue.task.callable.CallableFactorialTask; +import com.baeldung.concurrent.threadreturnvalue.task.fork.ForkExecutor; +import com.baeldung.concurrent.threadreturnvalue.task.fork.ForkFactorialTask; + +public class ForkUnitTest { + + private final ForkExecutor forkExecutor = new ForkExecutor(); + + @Test + void givenForkExecutor_whenExecuteRecursiveTask_thenResultOk() { + assertEquals(BigInteger.valueOf(3628800), forkExecutor.execute(new ForkFactorialTask(10, 5))); + } + + @Test + void givenForkExecutor_whenExecuteCallable_thenResultOk() { + assertEquals(BigInteger.valueOf(126), forkExecutor.execute(Arrays.asList(new CallableFactorialTask(5), new CallableFactorialTask(3)))); + } +}