java8 CompletableFuture异步调用与lamda结合

由于公司业务重度使用的lamda表达式,但是每次数据量增大之后,即使是同一模块调用也接近1s。所以开始学下lamda表达式

异步执行动作

static void thenApplyAsyncExample() {
    CompletableFuture<String>cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
    assertTrue(Thread.currentThread().isDaemon());
    randomSleep();
    returns.toUpperCase();
    });
    assertNull(cf.getNow(null));
    assertEquals("MESSAGE", cf.join());
}

使用固定的线程池完成异步执行动作

可以通过使用线程池方式来管理异步动作申请,以下代码基于固定的线程池,也是做一个大写字母转换动作

staticExecutorService 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<String>cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
            assertTrue(Thread.currentThread().getName().startsWith("custom-executor-"));
            assertFalse(Thread.currentThread().isDaemon());
            randomSleep();
            returns.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);
}

消费者是同步执行的,所以不需要在 CompletableFuture 里对结果进行合并。

异步消费

相较于前一个示例的同步方式,我们也对应有异步方式,代码如清单 11 所示。

static void thenAcceptAsyncExample() {
    StringBuilder result = new StringBuilder();
    CompletableFuture<Void>cf = CompletableFuture.completedFuture("thenAcceptAsync message")
    .thenAcceptAsync(s ->result.append(s));
    cf.join();
    assertTrue("Result was empty", result.length() > 0);
}

计算过程中的异常
接下来介绍异步操作过程中的异常情况处理。下面这个示例中我们会在字符转换异步请求中刻意延迟 1 秒钟,然后才会提交到 ForkJoinPool 里面去执行。

static void completeExceptionallyExample() {
        CompletableFuture<String>cf = CompletableFuture.completedFuture("message").thenApplyAsync(String::toUpperCase,
        CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS));
        CompletableFuture<String>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(计算完毕),然后调用 thenApplyAsync 返回一个新的 CompletableFuture,接着通过使用 delayedExecutor(timeout, timeUnit)方法延迟 1 秒钟执行。然后我们创建一个 handler(exceptionHandler),它会处理异常,返回另一个字符串"message upon cancel"。接下来进入 join()方法,执行大写转换操作,并且抛出 CompletionException 异常。

取消计算任务

与前面一个异常处理的示例类似,我们可以通过调用 cancel(boolean mayInterruptIfRunning)方法取消计算任务。此外,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());
}

一个 CompletableFuture VS 两个异步计算

我们可以创建一个 CompletableFuture 接收两个异步计算的结果,下面代码首先创建了一个 String 对象,接下来分别创建了两个 CompletableFuture 对象 cf1 和 cf2,cf2 通过调用 applyToEither 方法实现我们的需求。

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

运行两个阶段后执行

下面这个示例程序两个阶段执行完毕后返回结果,首先将字符转为大写,然后将字符转为小写,在两个计算阶段都结束之后触发 CompletableFuture

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

整合两个计算结果

我们可以通过 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));
}

按照同步方式执行两个方法后再合成字符串,以下代码采用异步方式同步执行两个方法,由于异步方式情况下不能够确定哪一个方法最终执行完毕,所以我们需要调用 join()方法等待后一个方法结束后再合成字符串,这一点和线程的 join()方法是一致的,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到 join()方法了,即 join()的作用是:"等待该线程终止"。

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

除了 thenCombine()方法以外,还有另外一种方法-thenCompose(),这个方法也会实现两个方法执行后的返回结果的连接。

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

anyOf()方法

以下代码模拟了如何在几个计算过程中任意一个完成后创建 CompletableFuture,在这个例子中,我们创建了几个计算过程,然后转换字符串到大写字符。由于这些 CompletableFuture 是同步执行的(下面这个例子使用的是 thenApply()方法,而不是 thenApplyAsync()方法),使用 anyOf()方法后返回的任何一个值都会立即触发 CompletableFuture。然后我们使用 whenComplete(BiConsumer<? super Object, ? super Throwable> action)方法处理结果。

static void anyOfExample() {
    StringBuilder result = new StringBuilder();
    List messages = Arrays.asList("a", "b", "c");
    List<CompletableFuture> 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);
}

当所有的 CompletableFuture 完成后创建 CompletableFuture
清单 22 所示我们会以同步方式执行多个异步计算过程,在所有计算过程都完成后,创建一个 CompletableFuture。

static void allOfExample() {
    StringBuilder result = new StringBuilder();
    List messages = Arrays.asList("a", "b", "c");
    List<CompletableFuture> 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<CompletableFuture> 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);
}

首先异步地通过调用 cars()方法获取 Car 对象,返回一个 CompletionStage<List>实例。Cars()方法可以在内部使用调用远端服务器上的 REST 服务等类似场景。
然后和其他的 CompletionStage<List>组合,通过调用 rating(manufacturerId)方法异步地返回 CompletionStage 实例。
当所有的 Car 对象都被填充了 rating 后,调用 allOf()方法获取最终值。
调用 whenComplete()方法打印最终的评分(rating)。

cars().thenCompose(cars -> {
    List<CompletionStage> 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();

自己改造的lamda+CompletableFuture调用模板

public class FurtureTest {

    public  static void allOfExample() {
        StringBuilder result = new StringBuilder();
        List<String> messages = Arrays.asList("a", "b", "c");
        List<CompletableFuture> futures = messages.stream()
                .map(msg -> CompletableFuture.completedFuture(msg).thenApply(s ->result.append(s)))
                .collect(Collectors.toList());
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((v, th) -> {
            futures.forEach(cf -> { System.out.println("a");});
            result.append("done");
        });
    }

    public static String delayedLowerCase(String s){
        return s.toString();
    }
    public static void main(String[] argas){
        allOfExample();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249