diff --git a/.gitignore b/.gitignore index 6143e53..db111de 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# other +.idea +target + diff --git a/src/main/java/com/example/completablefuture/CompletableFutureExamples.java b/src/main/java/com/example/completablefuture/CompletableFutureExamples.java deleted file mode 100644 index c02e22e..0000000 --- a/src/main/java/com/example/completablefuture/CompletableFutureExamples.java +++ /dev/null @@ -1,269 +0,0 @@ -package com.example.completablefuture; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Arrays; -import java.util.List; -import java.util.Random; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -public class CompletableFutureExamples { - - static ExecutorService executor = Executors.newFixedThreadPool(3, new ThreadFactory() { - int count = 1; - - @Override - public Thread newThread(Runnable runnable) { - return new Thread(runnable, "custom-executor-" + count++); - } - }); - - static Random random = new Random(); - - public static void main(String[] args) { - try { -// allOfAsyncExample(); - } finally { - executor.shutdown(); - } - } - - static void completedFutureExample() { - CompletableFuture cf = CompletableFuture.completedFuture("message"); - assertTrue(cf.isDone()); - assertEquals("message", cf.getNow(null)); - } - - static void completeExceptionallyExample() { - CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(String::toUpperCase, - CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS)); - CompletableFuture exceptionHandler = cf.handle((s, th) -> { return (th != null) ? "message upon cancel" : ""; }); - cf.completeExceptionally(new RuntimeException("completed exceptionally")); - assertTrue("Was not completed exceptionally", cf.isCompletedExceptionally()); - try { - cf.join(); - fail("Should have thrown an exception"); - } catch (CompletionException ex) { // just for testing - assertEquals("completed exceptionally", ex.getCause().getMessage()); - } - - assertEquals("message upon cancel", exceptionHandler.join()); - } - - static void runAsyncExample() { - CompletableFuture cf = CompletableFuture.runAsync(() -> { - assertTrue(Thread.currentThread().isDaemon()); - randomSleep(); - }); - assertFalse(cf.isDone()); - sleepEnough(); - assertTrue(cf.isDone()); - } - - static void thenApplyExample() { - CompletableFuture cf = CompletableFuture.completedFuture("message").thenApply(s -> { - assertFalse(Thread.currentThread().isDaemon()); - return s.toUpperCase(); - }); - assertEquals("MESSAGE", cf.getNow(null)); - } - - static void thenApplyAsyncExample() { - CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> { - assertTrue(Thread.currentThread().isDaemon()); - randomSleep(); - return s.toUpperCase(); - }); - assertNull(cf.getNow(null)); - assertEquals("MESSAGE", cf.join()); - } - - static void thenApplyAsyncWithExecutorExample() { - CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> { - assertTrue(Thread.currentThread().getName().startsWith("custom-executor-")); - assertFalse(Thread.currentThread().isDaemon()); - randomSleep(); - return s.toUpperCase(); - }, executor); - - assertNull(cf.getNow(null)); - assertEquals("MESSAGE", cf.join()); - } - - static void thenAcceptExample() { - StringBuilder result = new StringBuilder(); - CompletableFuture.completedFuture("thenAccept message") - .thenAccept(s -> result.append(s)); - assertTrue("Result was empty", result.length() > 0); - } - - static void thenAcceptAsyncExample() { - StringBuilder result = new StringBuilder(); - CompletableFuture cf = CompletableFuture.completedFuture("thenAcceptAsync message") - .thenAcceptAsync(s -> result.append(s)); - cf.join(); - assertTrue("Result was empty", result.length() > 0); - } - - static void cancelExample() { - CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(String::toUpperCase, - CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS)); - CompletableFuture cf2 = cf.exceptionally(throwable -> "canceled message"); - assertTrue("Was not canceled", cf.cancel(true)); - assertTrue("Was not completed exceptionally", cf.isCompletedExceptionally()); - assertEquals("canceled message", cf2.join()); - } - - static void applyToEitherExample() { - String original = "Message"; - CompletableFuture cf1 = CompletableFuture.completedFuture(original) - .thenApplyAsync(s -> delayedUpperCase(s)); - CompletableFuture cf2 = cf1.applyToEither( - CompletableFuture.completedFuture(original).thenApplyAsync(s -> delayedLowerCase(s)), - s -> s + " from applyToEither"); - assertTrue(cf2.join().endsWith(" from applyToEither")); - } - - static void acceptEitherExample() { - String original = "Message"; - StringBuilder result = new StringBuilder(); - CompletableFuture cf = CompletableFuture.completedFuture(original) - .thenApplyAsync(s -> delayedUpperCase(s)) - .acceptEither(CompletableFuture.completedFuture(original).thenApplyAsync(s -> delayedLowerCase(s)), - s -> result.append(s).append("acceptEither")); - cf.join(); - assertTrue("Result was empty", result.toString().endsWith("acceptEither")); - } - - static void runAfterBothExample() { - String original = "Message"; - StringBuilder result = new StringBuilder(); - CompletableFuture.completedFuture(original).thenApply(String::toUpperCase).runAfterBoth( - CompletableFuture.completedFuture(original).thenApply(String::toLowerCase), - () -> result.append("done")); - assertTrue("Result was empty", result.length() > 0); - } - - static void thenAcceptBothExample() { - String original = "Message"; - StringBuilder result = new StringBuilder(); - CompletableFuture.completedFuture(original).thenApply(String::toUpperCase).thenAcceptBoth( - CompletableFuture.completedFuture(original).thenApply(String::toLowerCase), - (s1, s2) -> result.append(s1 + s2)); - assertEquals("MESSAGEmessage", result.toString()); - } - - static void thenCombineExample() { - String original = "Message"; - CompletableFuture cf = CompletableFuture.completedFuture(original).thenApply(s -> delayedUpperCase(s)) - .thenCombine(CompletableFuture.completedFuture(original).thenApply(s -> delayedLowerCase(s)), - (s1, s2) -> s1 + s2); - assertEquals("MESSAGEmessage", cf.getNow(null)); - } - - static void thenCombineAsyncExample() { - String original = "Message"; - CompletableFuture cf = CompletableFuture.completedFuture(original) - .thenApplyAsync(s -> delayedUpperCase(s)) - .thenCombine(CompletableFuture.completedFuture(original).thenApplyAsync(s -> delayedLowerCase(s)), - (s1, s2) -> s1 + s2); - assertEquals("MESSAGEmessage", cf.join()); - } - - static void thenComposeExample() { - String original = "Message"; - CompletableFuture cf = CompletableFuture.completedFuture(original).thenApply(s -> delayedUpperCase(s)) - .thenCompose(upper -> CompletableFuture.completedFuture(original).thenApply(s -> delayedLowerCase(s)) - .thenApply(s -> upper + s)); - assertEquals("MESSAGEmessage", cf.join()); - } - - static void anyOfExample() { - StringBuilder result = new StringBuilder(); - List messages = Arrays.asList("a", "b", "c"); - List> futures = messages.stream() - .map(msg -> CompletableFuture.completedFuture(msg).thenApply(s -> delayedUpperCase(s))) - .collect(Collectors.toList()); - CompletableFuture.anyOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((res, th) -> { - if(th == null) { - assertTrue(isUpperCase((String) res)); - result.append(res); - } - }); - assertTrue("Result was empty", result.length() > 0); - } - - static void allOfExample() { - StringBuilder result = new StringBuilder(); - List messages = Arrays.asList("a", "b", "c"); - List> futures = messages.stream() - .map(msg -> CompletableFuture.completedFuture(msg).thenApply(s -> delayedUpperCase(s))) - .collect(Collectors.toList()); - CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((v, th) -> { - futures.forEach(cf -> assertTrue(isUpperCase(cf.getNow(null)))); - result.append("done"); - }); - assertTrue("Result was empty", result.length() > 0); - } - - static void allOfAsyncExample() { - StringBuilder result = new StringBuilder(); - List messages = Arrays.asList("a", "b", "c"); - List> futures = messages.stream() - .map(msg -> CompletableFuture.completedFuture(msg).thenApplyAsync(s -> delayedUpperCase(s))) - .collect(Collectors.toList()); - CompletableFuture allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])) - .whenComplete((v, th) -> { - futures.forEach(cf -> assertTrue(isUpperCase(cf.getNow(null)))); - result.append("done"); - }); - allOf.join(); - assertTrue("Result was empty", result.length() > 0); - } - - private static boolean isUpperCase(String s) { - for (int i = 0; i < s.length(); i++) { - if (Character.isLowerCase(s.charAt(i))) { - return false; - } - } - return true; - } - - private static String delayedUpperCase(String s) { - randomSleep(); - return s.toUpperCase(); - } - - private static String delayedLowerCase(String s) { - randomSleep(); - return s.toLowerCase(); - } - - private static void randomSleep() { - try { - Thread.sleep(random.nextInt(1000)); - } catch (InterruptedException e) { - // ... - } - } - - private static void sleepEnough() { - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - // ... - } - } - -} diff --git a/src/main/java/com/example/completablefuture/CompletableFutureExamples2.java b/src/main/java/com/example/completablefuture/CompletableFutureExamples2.java new file mode 100644 index 0000000..89dc3ee --- /dev/null +++ b/src/main/java/com/example/completablefuture/CompletableFutureExamples2.java @@ -0,0 +1,37 @@ +package com.example.completablefuture; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class CompletableFutureExamples2 { + public static void main(String[] args) { + + System.out.println("Beginning of the program"); + + CompletableFuture.supplyAsync(new Supplier() { + + @Override + public Integer get() { + return longNetworkProcess(5); + } + }).thenAccept(value -> System.out.println(value)); + + sleep(5); + System.out.println("End of the program"); + } + + public static int longNetworkProcess(int value) { + sleep(3); + + return value * 10; + } + + public static void sleep(int seconds) { + try { + Thread.sleep(seconds * 1000); + } + catch(InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/example/completablefuture/RealLifeCompletableFutureExample.java b/src/main/java/com/example/completablefuture/RealLifeCompletableFutureExample.java index e27a531..a6357a3 100644 --- a/src/main/java/com/example/completablefuture/RealLifeCompletableFutureExample.java +++ b/src/main/java/com/example/completablefuture/RealLifeCompletableFutureExample.java @@ -12,6 +12,8 @@ public static void main(String[] args) { long start = System.currentTimeMillis(); cars().thenCompose(cars -> { + + // set the rating of each car List> updatedCars = cars.stream() .map(car -> rating(car.manufacturerId).thenApply(r -> { car.setRating(r); @@ -20,8 +22,10 @@ public static void main(String[] args) { CompletableFuture done = CompletableFuture .allOf(updatedCars.toArray(new CompletableFuture[updatedCars.size()])); + return done.thenApply(v -> updatedCars.stream().map(CompletionStage::toCompletableFuture) .map(CompletableFuture::join).collect(Collectors.toList())); + }).whenComplete((cars, th) -> { if (th == null) { cars.forEach(System.out::println); @@ -43,6 +47,7 @@ static CompletionStage rating(int manufacturer) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } + switch (manufacturer) { case 2: return 4f; @@ -61,6 +66,7 @@ static CompletionStage> cars() { carList.add(new Car(1, 3, "Fiesta", 2017)); carList.add(new Car(2, 7, "Camry", 2014)); carList.add(new Car(3, 2, "M2", 2008)); + return CompletableFuture.supplyAsync(() -> carList); } diff --git a/src/main/java/com/example/completablefuture/RealLifeWithoutCompletableFutureExample.java b/src/main/java/com/example/completablefuture/RealLifeWithoutCompletableFutureExample.java index 09f11cd..ee5cdf6 100644 --- a/src/main/java/com/example/completablefuture/RealLifeWithoutCompletableFutureExample.java +++ b/src/main/java/com/example/completablefuture/RealLifeWithoutCompletableFutureExample.java @@ -3,31 +3,46 @@ import java.util.ArrayList; import java.util.List; +/** + * In this example, the only thread running is the + * main thread. It sleeps for 2 seconds each time + * the setRating() method is called + */ public class RealLifeWithoutCompletableFutureExample { public static void main(String[] args) { long start = System.currentTimeMillis(); + // creating a list of cars List cars = cars(); + + Long cars_data_processing_start = System.currentTimeMillis(); + // setting the rating of each car cars.forEach(car -> { float rating = rating(car.manufacturerId); car.setRating(rating); }); + Long cars_data_processing_end = System.currentTimeMillis(); + // printing cars cars.forEach(System.out::println); long end = System.currentTimeMillis(); - System.out.println("Took " + (end - start) + " ms."); + System.out.println("The cars data processing to set its ratings took : " + + (cars_data_processing_end - cars_data_processing_start) + " ms."); + System.out.println("The program took " + (end - start) + " ms."); } static float rating(int manufacturer) { try { simulateDelay(); + System.out.println("Thread : " + Thread.currentThread().getName()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } + switch (manufacturer) { case 2: return 4f; @@ -49,7 +64,7 @@ static List cars() { } private static void simulateDelay() throws InterruptedException { - Thread.sleep(5000); + Thread.sleep(2000); } }