当前位置: 代码迷 >> 综合 >> Java 异步编程--CompletableFuture
  详细解决方案

Java 异步编程--CompletableFuture

热度:30   发布时间:2023-12-23 09:41:51.0

Java 异步编程

Java 1.5开始,提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
Future接口可以构建异步应用,是多线程开发中常见的设计模式。
当我们需要调用一个函数方法时。如果这个函数执行很慢,那么我们就要进行等待。但有时候,我们可能并不急着要结果。
因此,我们可以让被调用者立即返回,让他在后台慢慢处理这个请求。对于调用者来说,则可以先处理一些其他任务,在真正需要数据的场合再去尝试获取需要的数据。

本文使用了子线程主线程的描述方式,但是可能并没有出现线程相关的东西,这样描述只是为了区分。子线程就是异步任务,主线程就是执行异步任务后接下来的任务

对于Callable和Future的使用可见下面这个链接,你会在运行的时候发现代码不会因为子线程的阻塞而使主线程等待
Future、callable 使用

相对于future ,jdk1.8有推出了CompletableFuture,那么两者有神们特性上的区别呢

了解了Future的使用,这里就要谈谈Future的局限性。Future很难直接表述多个Future 结果之间的依赖性,开发中,我们经常需要达成以下目的:
1.将两个异步计算合并为一个(这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果)
2.等待 Future 集合中的所有任务都完成。
3.仅等待 Future 集合中最快结束的任务完成,并返回它的结果。
而且对于future:我们的目的都是获取异步任务的结果,但是对于Future来说,只能通过get方法或者死循环判断isDone来获取。异常情况就更是难办。对CompletableFuture只要我们设置好回调函数即可实现:
1.只要任务完成,即执行我们设置的函数(不用再去考虑什么时候任务完成)
2.如果发生异常,同样会执行我们处理异常的函数,甚至连默认返回值都有(异常情况处理更加省力)
3.如果有复杂任务,比如依赖问题,组合问题等,同样可以写好处理函数来处理(能应付复杂任务的处理)

CompletableFuture的详解

CompletableFuture1
CompletableFuture2

无意做一个搬运工,但是人家已经写得很好了,就不再造轮子了

执行异步任务(供给型)

      //join 相当于从从Future中取值,这样会造成阻塞Integer join = CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    Thread.sleep(1000);return "1212";}}).thenApplyAsync(new Function<String, Integer>() {
    @SneakyThrows@Overridepublic Integer apply(String s) {
    Thread.sleep(10000);return Integer.parseInt(s);}}).join();System.out.println("主线程");System.out.println(join);
 //体现异步的地方在于 CompletableFuture.supplyAsync 没有返回结果,//但是主线程的System.out.println("前面的额阻塞,主线程可以执行");依然执行了CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    Thread.sleep(1000);return "1212";}}).thenApplyAsync(new Function<String, Integer>() {
    @SneakyThrows@Overridepublic Integer apply(String s) {
    Thread.sleep(10000);return Integer.parseInt(s);}});System.out.println("前面的额阻塞,主线程可以执行");System.out.println(completableFuture.get());

上面CompletableFuture代码中和Future不同的地方,可以总结为以下两点:
1.不用关心第一个异步和第二个异步到底什么时候完成,完成了肯定会执行回调函数,
2.对于上面这个两个有依赖的异步任务Future做起来就很费劲

执行异步任务(消费型)

  CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    Thread.sleep(1900);return "I am rich man";}}).thenAcceptAsync(new Consumer<String>() {
    @Overridepublic void accept(String s) {
    System.out.println(s);}});System.out.println("主线程");

合作依赖性异步任务(结合两个CompletionStage的结果,进行转化后返回)

combine的这两个阶段是并行执行的,然后等这两个阶段都执行完了再执行 那个BiFunction<String, String, String>(),效果类似于同步编程中的三个线程,join等待执行的额效果 可参见如下文章
Java让主线程等待执行的方法

   CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    Thread.sleep(1000);return "hell0";}}).thenCombine(CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    Thread.sleep(1000);return "world";}}), new BiFunction<String, String, String>() {
    @Overridepublic String apply(String s, String s2) {
    return s + s2;}});System.out.println("主线程");System.out.println(future.get());

两种渠道完成同一个事情,就可以调用这个方法,找一个最快的结果进行处理,最终有返回值。

      String result = CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    Thread.sleep(1);return "Hi Boy";}}).applyToEither(CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    // Thread.sleep(300);return "Hi Girl";}}), new Function<String, String>() {
    @Overridepublic String apply(String s) {
    return s;}}).join();System.out.println(result);

运行时出现了异常,可以通过exceptionally进行补偿

 String result = CompletableFuture.supplyAsync(new Supplier<String>() {
    @SneakyThrows@Overridepublic String get() {
    Thread.sleep(100);if (true) {
    throw new RuntimeException("exception test!");}return "Hi Boy";}}).exceptionally(new Function<Throwable, String>() {
    @Overridepublic String apply(Throwable e) {
    System.out.println(e.getMessage());return "Hello world!";}}).join();System.out.println(result);

关于异步编程的错误处理还很多,请参看上面的链接,我用到再补充

  相关解决方案