java future 例子_使用Java的CompletableFuture的20个示例

1.创建一个完整的CompletableFuture

最简单的示例创建一个已经完成CompletableFuture并具有预定义结果的结果。通常,这可能是您计算的开始阶段。

static void completedFutureExample() {

CompletableFuture cf = CompletableFuture.completedFuture("message");

assertTrue(cf.isDone());

assertEquals("message", cf.getNow(null));

}

该getNow(null)如果完成的结果返回(这显然是这种情况),但否则返回NULL(参数)。

50cd38463cf07a2e7cb64cd47813f264.png

2.运行一个简单的异步阶段

下一个示例是如何创建一个Runnable异步执行的阶段:

static void runAsyncExample() {

CompletableFuture cf = CompletableFuture.runAsync(() -> {

assertTrue(Thread.currentThread().isDaemon());

randomSleep();

});

assertFalse(cf.isDone());

sleepEnough();

assertTrue(cf.isDone());

}

该示例的要点是两件事:

1. 当方法通常以关键字结尾时,将异步执行CompletableFuture Async

2. 默认情况下(未Executor指定no时),异步执行使用通用ForkJoinPool实现,该通用实现使用守护程序线程执行Runnable任务。请注意,这特定于CompletableFuture。其他CompletionStage实现可以覆盖默认行为。

3.在上一阶段应用功能

下面的示例采用CompletableFuture示例#1 的完成内容,其中包含结果字符串,"message" 并应用将其转换为大写字母的函数:

static void thenApplyExample() {

CompletableFuture cf = CompletableFuture.completedFuture("message").thenApply(s -> {

assertFalse(Thread.currentThread().isDaemon());

return s.toUpperCase();

});

assertEquals("MESSAGE", cf.getNow(null));

}

注意以下行为关键字thenApply:

1. then,这意味着该阶段的操作在当前阶段正常完成时发生(无例外)。在这种情况下,当前阶段已经完成,值为“ message”。

2. Apply,这意味着返回的阶段将对Function上一个阶段的结果应用a 。

Functionwill 的执行将被阻塞,这意味着只有在完成大写操作后才能到达getNow()。

4. Function在上一阶段异步应用a

通过将Async后缀附加到上一个示例中的方法,链接的链CompletableFuture将异步执行(使用ForkJoinPool.commonPool())。

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());

}

5.使用自定义执行器在上一阶段异步应用功能

异步方法的一个非常有用的功能是能够提供一个Executor使用它来执行所需的函数的能力CompletableFuture。此示例说明如何使用固定线程池应用大写转换Function:

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 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());

}

6.消耗上一阶段的结果

如果下一阶段接受当前阶段的结果,但不需要在计算中返回值(即,其返回类型为void),则Function可以应用a ,而不是应用a Consumer,因此该方法为thenAccept:

static void thenAcceptExample() {

StringBuilder result = new StringBuilder();

CompletableFuture.completedFuture("thenAccept message")

.thenAccept(s -> result.append(s));

assertTrue("Result was empty", result.length() > 0);

}

在Consumer将同步执行,所以我们并不需要加入对返回CompletableFuture。

7.异步消耗上一阶段的结果

同样,使用的异步版本thenAccept,链式CompletableFuture将异步执行:

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);

}

8.异常完成计算

现在,让我们看看如何异常地显式完成异步操作,从而指示计算失败。为简单起见,该操作采用一个字符串并将其转换为大写,并且我们模拟了1秒的操作延迟。为此,我们将使用该thenApplyAsync(Function, Executor)方法,其中第一个参数是大写函数,而执行程序是延迟的执行程序,在实际将操作提交给common之前,它会等待1秒钟ForkJoinPool。

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());

}

让我们详细研究这个示例:

· 首先,我们创建一个CompletableFuture已经使用value完成的"message"。接下来,我们调用thenApplyAsync,它返回一个new CompletableFuture。此方法在第一阶段完成时(已经完成,因此Function将立即执行)以异步方式应用大写转换。此示例还说明了使用该delayedExecutor(timeout, timeUnit)方法延迟异步任务的方法。

· 然后,我们创建一个单独的“ handler”阶段exceptionHandler,该阶段通过返回另一条消息来处理任何异常"message upon cancel"。

· 接下来,我们明确地完成了第二阶段,但有一个例外。这使得join()正在执行大写操作的舞台上的方法抛出a CompletionException(通常join()将等待1秒以获取大写字符串)。它还将触发处理程序阶段。

9.取消计算

非常接近完成,我们可以通过界面上的cancel(boolean mayInterruptIfRunning)方法取消计算Future。对于CompletableFuture,不使用boolean参数,因为实现不使用中断来进行取消。而是cancel()等效于completeExceptionally(new CancellationException())。

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());

}

10.将函数应用于两个完成阶段之一的结果

下面的示例创建一个CompletableFuture将a应用于Function前两个阶段中任一阶段的结果(不保证将其中一个传递给Function)。有问题的两个阶段是:一个将大写转换应用于原始字符串,另一个将小写转换应用于:

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"));

}

11.消费两个完成阶段中任何一个的结果

与上一个示例类似,但是使用a Consumer代替a Function(依赖项CompletableFuture的类型为void):

static void acceptEitherExample() {

String original = "Message";

StringBuffer result = new StringBuffer();

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"));

}

注意这里使用线程安全 StringBuffer 而不是 StringBuilder。

12.在两个阶段完成时运行可运行文件

此示例显示了两个阶段都完成时CompletableFuture执行Runnable触发器的依赖项。请注意,下面的所有阶段都是同步运行的,其中一个阶段首先将消息字符串转换为大写,然后第二阶段将相同消息字符串转换为小写。

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);

}

13.在BiConsumer中接受两个阶段的结果

如果需要Runnable,可以使用而不是在两个阶段均完成时执行BiConsumer:

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());

}

14.在两个阶段的结果上应用BiFunction

如果依赖项CompletableFuture旨在通过对两个前一个CompletableFutures 的结果应用函数并返回结果来合并结果,则可以使用method thenCombine()。整个流水线是同步的,因此getNow()最后将检索最终结果,即大写和小写结果的串联。

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));

}

15.在两个阶段的结果上异步应用BiFunction

与前面的示例相似,但是具有不同的行为:由于两个CompletableFuture依赖的两个阶段都异步运行,因此该thenCombine()方法即使没有Async后缀也异步执行。这在Javadocs类中得到了记录:“为非异步方法的相关完成提供的操作可以由完成当前CompletableFuture的线程或完成方法的任何其他调用者执行。” 因此,我们需要join()对合并CompletableFuture进行等待结果。

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());

}

16.组成CompletableFutures

我们可以使用组合使用thenCompose()来完成与前两个示例相同的计算。此方法等待第一阶段(应用大写转换)完成。其结果传递到指定的结果,该结果Function返回a CompletableFuture,其结果将是返回的结果CompletableFuture。在这种情况下,函数采用大写字符串(upper),然后返回CompletableFuture将original字符串转换成小写形式的,然后将其附加到upper。

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());

}

17.创建一个可以在多个阶段中的任何一个完成时完成的阶段

以下示例说明了如何创建一个CompletableFuture在多个CompletableFutures中的任何一个完成时都完成且结果相同的。首先创建几个阶段,每个阶段都将字符串从列表转换为大写。由于所有这些CompletableFuture都正在同步执行(使用thenApply()),因此CompletableFuture从返回的内容anyOf()将立即执行,因为到调用它时,所有阶段都已完成。然后whenComplete(BiConsumer super Object, ? super Throwable> action),我们使用来处理结果(断言结果为大写)。

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);

}

18.创建一个阶段,当所有阶段完成时完成

接下来的两个示例说明了如何分别以同步和异步的方式创建一个CompletableFuture同时完成多个CompletableFutures的完整的。该方案与前面的示例相同:提供了一个字符串列表,其中每个元素都转换为大写。

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);

}

19.创建一个在所有阶段都完成时异步完成的阶段

通过切换到thenApplyAsync()单个CompletableFutures,返回的阶段由allOf()完成阶段的公共池线程之一执行。因此,我们需要调用join()它以等待其完成。

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);

}

20.现实生活中的例子

现在已经探究了CompletionStage和的功能CompletableFuture,下面的示例将它们应用于实际情况:

1. 首先,Car通过调用cars()方法异步获取对象列表,该方法返回CompletionStage。该cars()方法可能会在后台消耗远程REST端点。

2. 然后CompletionStage,我们通过调用rating(manufacturerId)返回CompletionStage异步获取汽车额定值的方法(又可能会消耗REST端点)来组成另一个用于填充每个汽车额定值的方法。

3. 当所有Car对象都充满其评价时,我们以结束List,因此我们调用allOf()以获取最后一个阶段(存储在variable中done),该阶段在所有这些阶段完成时完成。

4. 使用whenComplete()在最后阶段,我们打印出Car他们的评级对象。

cars().thenCompose(cars -> {

List> updatedCars = cars.stream()

.map(car -> rating(car.manufacturerId).thenApply(r -> {

car.setRating(r);

return car;

})).collect(Collectors.toList());

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);

} else {

throw new RuntimeException(th);

}

}).toCompletableFuture().join();

由于Car所有实例都是独立的,因此异步获取每个评级可以提高性能。此外,与使用allOf()手动线程等待(例如使用Thread#join()或CountDownLatch)相反,使用更自然的方法来等待所有汽车额定值被填满。

最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。

a2e6694ff87afec757bc46cd6864eef5.png

e713f68dd3623d9dfe5be7d93fba410e.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值