CompletableFuture

一、CompletableFuture介绍

CompletableFuture是Java 8提供的一种新的编写异步任务的方式,它是一个受检查的异步任务的执行器。它支持多个任务的编排,并提供了用于检查任务执行状态以及支持完成时的回调接口的机制。通过使用CompletableFuture,程序员可以更加灵活地组织异步任务,减少代码量,从而提高程序的性能和效率。

CompletableFuture的主要特点包括:

  • 异步执行:它支持异步执行,可以使用supplyAsync()等方法来创建一个异步执行的任务,并返回一个CompletableFuture对象。当任务结束后,可以使用get()方法获取返回值,该方法会阻塞等待任务执行结果。
  • 异步结果处理:它允许使用thenApply()、thenAccept()、thenRun()等方法对异步操作的结果进行处理。这些方法使用函数式接口作为参数,可以将异步结果传递给下一个操作,并生成新的CompletableFuture对象。
  • 组合操作:CompletableFuture能够方便地组合多个异步任务,以实现任务的顺序或并行关系。例如,使用thenCombine、thenCompose等方法可以等待多个异步任务的结果,并在所有任务完成后进行处理。

CompletableFuture的使用场景非常广泛,包括但不限于:

  • 并行处理多个独立任务:当任务可以被分解为多个独立的子任务时,可以使用CompletableFuture来并行执行这些子任务,以提高系统的性能和响应速度。例如,在电商系统中,查询用户信息、订单信息、购物车信息等可以并行执行,然后在所有子任务完成后进行结果合并。
  • 异步执行耗时操作:对于耗时的操作,如远程调用、数据库查询等,可以使用CompletableFuture来异步执行这些操作,避免阻塞主线程,提高系统的吞吐量和并发能力。

总的来说,CompletableFuture为Java程序员提供了一种强大而灵活的工具,用于编写高效、可维护的异步代码。它大大简化了异步编程的复杂性,提高了代码的可读性和可维护性,是现代Java应用开发中不可或缺的一部分。

二、CompletableFuture实际使用

1、创建异步任务

CompletableFuture提供了supplyAsync和runAsync两种方式创建异步任务。并且都提供了支持默认线程池以及自定义线程池的实现。

supplyAsync方法用于创建一个带有返回值的异步任务。它接收一个实现了Supplier接口的对象作为参数,这个接口定义了一个没有参数且返回类型为U的get()方法。当调用supplyAsync时,它会将Supplier的get()方法提交给一个异步任务去执行,并立即返回一个CompletableFuture对象,这个对象代表了异步计算的结果。

runAsync方法则用于创建一个没有返回值的异步任务。它接收一个实现了Runnable接口的对象作为参数,这个接口定义了一个没有参数且没有返回值的run()方法。调用runAsync时,它会将Runnable的run()方法提交给一个异步任务去执行。

1.1 supplyAsync

下面显示了默认线程池和自定义线程池的两种写法,CompletableFuture的方法基本都支持使用自定义线程池和默认线程池。如果你需要更灵活地控制线程池的配置和管理,或者你的应用程序有特殊的线程需求,那么推荐使用自定义线程池提交任务。如果你只是想简单地异步执行任务,并且不需要过多的线程控制,那么使用CompletableFuture.runAsync可能是一个更好的选择。

// 带返回值异步请求,默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
 
// 带返回值的异步请求,可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

@Test
public void supplyAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
        //异步执行该线程中的方法
        System.out.println("execute task");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("execute success");
        return "task execute result";
    });
    //等待任务执行完成 cf.get()的时候主线程会开始阻塞,直到任务完成
    System.out.println("获取任务结果->" + cf.get());
    System.out.println("已获取到任务执行结果,继续后续逻辑");
}

@Test
public void supplyAsyncExecutor() throws ExecutionException, InterruptedException {
    // 自定义线程池
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
        //异步执行该线程中的方法
        System.out.println("execute task");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("execute success");
        return "task execute result";
    },executorService);
    //等待任务执行完成 cf.get()的时候主线程会开始阻塞,直到任务完成
    System.out.println("获取任务结果->" + cf.get());
    System.out.println("已获取到任务执行结果,继续后续逻辑");
}

1.2 runAsync

// 不带返回值的异步请求,默认线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
 
// 不带返回值的异步请求,可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)


/**
 *  只要调用CompletableFuture.runAsync()返回值的get方法就会导致阻塞
 */
@Test
public void runAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
        //异步执行该线程中的方法
        System.out.println("execute task");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("execute success");
    });
    //也可以调用cf.get()同样会导致阻塞,用于等待cf任务执行完成
    System.out.println(cf.get());
}
/**
 *  只要调用CompletableFuture.runAsync()返回值的get方法就会导致阻塞
 */
@Test
public void runAsyncExecutor() throws ExecutionException, InterruptedException {
    // 自定义线程池
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
        //异步执行该线程中的方法
        System.out.println("execute task");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("execute success");
    },executorService);
    //也可以调用cf.get()同样会导致阻塞,用于等待cf任务执行完成
    System.out.println(cf.get());
}

1.3 获取任务结果

上述例子可以看到使用get()方法获取任务返回值,还支持很多方法获取返回值,如下:

//如果完成则返回结果
public T get() throws InterruptedException, ExecutionException 
 
//最大时间等待返回结果
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
 
//如果完成则返回结果,它不会抛出经过检查的异常(如 InterruptedException 或 ExecutionException),
//而是将这些异常封装在一个未检查的异常 CompletionException 中抛出。
//这意味着你不需要使用 try-catch 块来捕获这些经过检查的异常,但你需要确保你的代码能够处理可能抛出的 CompletionException。
public T join()
 
//如果完成则返回结果值,否则返回给定的 valueIfAbsent。
//需要注意的是,getNow 并不会抛出 CompletionException 或任何其它由异步任务中抛出的异常。
//它只是简单地返回异步任务的结果(如果已经完成的话)或者一个默认值。
public T getNow(T valueIfAbsent)
 
//如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
 
//如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex) 

2、异步任务结果回调处理

2.1 thenApply和thenApplyAsync

thenApply 表示某个任务执行完成后执行的动作,即异步任务的回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,带有返回值。

并且thenApply和thenApplyAsync区别在于,使用thenApply方法时子任务与父任务使用的是同一个线程,而thenApplyAsync在子任务中是另起一个线程执行任务,并且thenApplyAsync可以自定义线程池,默认的使用ForkJoinPool.commonPool()线程池。

/**
 * 注:cf1 异步线程执行完成后,会触发 cf2 的执行,但此时不意味着 cf2 会立即执行,cf2的实际执行时间取决于线程池的调度和系统的负载。
 * 如果希望主线程等待整个 CompletableFuture 链完成,
 * 需要调用 cf2.join() 或 cf1.get()(由于 cf2 依赖于 cf1 的完成,调用任何一个的 join 或 get 方法都会等待整个链完成)。
 * 如果不这样做,并且主线程没有其他任务要执行,那么程序可能会在主线程执行完毕后立即退出,而不等待异步任务完成。
 * 而且如果希望整个CompletableFuture链执行完成只需调用cf2.get()即可,无需调用cf1.get()。
 */
@Test
public void thenApply() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = cf1.thenApply((result) -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        result += 2;
        return result;
    });
    //阻塞等待任务1执行完成
    System.out.println("获取cf1结果->" + cf1.get());
    //阻塞等待任务2执行完成
    System.out.println("获取cf2结果->" + cf2.get());
}

@Test
public void thenApplyAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = cf1.thenApplyAsync((result) -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        result += 2;
        return result;
    });
    //等待任务1执行完成
    System.out.println("获取cf1结果->" + cf1.get());
    //等待任务2执行完成
    System.out.println("获取cf2结果->" + cf2.get());
}

2.2 thenAccept和thenAcceptAsync

thenAccep表示某个任务执行完成后执行的动作,即异步任务回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,无返回值。

并且thenAccep和thenAccepAsync区别在于,使用thenAccep方法时子任务与父任务使用的是同一个线程,而thenAccepAsync在子任务中可能是另起一个线程执行任务,并且thenAccepAsync可以自定义线程池,默认的使用ForkJoinPool.commonPool()线程池。

@Test
public void thenApplyAccept() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });
    CompletableFuture<Void> cf2 = cf1.thenAccept((result) -> {
        System.out.println(Thread.currentThread() + " cf1的结果:"+ result + " ,cf2 do something....");
    });
    //等待任务1执行完成
    System.out.println("获取cf1结果->" + cf1.get());
    //等待任务2执行完成
    System.out.println("获取cf2结果->" + cf2.get());
}

@Test
public void thenApplyAcceptAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });
    CompletableFuture<Void> cf2 = cf1.thenAcceptAsync((result) -> {
        System.out.println(Thread.currentThread() + " cf1的结果:"+ result + " ,cf2 do something....");
    });
    //等待任务1执行完成
    System.out.println("获取cf1结果->" + cf1.get());
    //等待任务2执行完成
    System.out.println("获取cf2结果->" + cf2.get());
}

2.3 thenRun和thenRunAsync

thenRun表示某个任务执行完成后执行的动作,即异步任务回调方法,无入参,无返回值。

并且thenRun和thenRunAsync区别在于,使用thenRun方法时子任务与父任务使用的是同一个线程,而thenRunAsync在子任务中可能是另起一个线程执行任务,并且thenRunAsync可以自定义线程池,默认的使用ForkJoinPool.commonPool()线程池。

@Test
public void thenRun() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Void> cf2 = cf1.thenRun(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
    });

    //等待任务1执行完成
    System.out.println("cf1结果->" + cf1.get());
    //等待任务2执行完成
    System.out.println("cf2结果->" + cf2.get());
}

@Test
public void thenRunAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Void> cf2 = cf1.thenRunAsync(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
    });

    //等待任务1执行完成
    System.out.println("cf1结果->" + cf1.get());
    //等待任务2执行完成
    System.out.println("cf2结果->" + cf2.get());
}

2.4 whenComplete和whenCompleteAsync

whenComplete是当某个任务执行完成后执行的回调方法,会将执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null,回调方法对应的CompletableFuture的result和该任务一致,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常。
whenCompleteAsync和whenComplete区别也是whenCompleteAsync可能会另起一个线程执行任务,并且thenRunAsync可以自定义线程池,默认的使用ForkJoinPool.commonPool()线程池。

@Test
public void whenComplete() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        int a = 1/0;
        return 1;
    });

    CompletableFuture<Integer> cf2 = cf1.whenComplete((result, e) -> {
        System.out.println("上个任务结果:" + result);
        System.out.println("上个任务抛出异常:" + e);
        System.out.println(Thread.currentThread() + " cf2 do something....");
    });

    //等待任务1执行完成
    System.out.println("cf1结果->" + cf1.get());
    //等待任务2执行完成
    System.out.println("cf2结果->" + cf2.get());
}

@Test
public void whenCompleteAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = cf1.whenCompleteAsync((result, e) -> {
        System.out.println("上个任务结果:" + result);
        System.out.println("上个任务抛出异常:" + e);
        System.out.println(Thread.currentThread() + " cf2 do something....");
    });

    //等待任务1执行完成
    System.out.println("cf1结果->" + cf1.get());
    //等待任务2执行完成
    System.out.println("cf2结果->" + cf2.get());
}

2.5 handle和handleAsync

跟whenComplete基本一致,区别在于handle的回调方法有返回值。
区别也是在于自定义线程池和默认线程池。

/**
 * 注:cf2中对cf1的返回结果进行操作时,注意判断是否异常,否则result是null时,再针对result进行某些操作可能会导致cf2中也会出现异常。
 */
@Test
public void handle() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        // int a = 1/0;
        return 1;
    });

    CompletableFuture<Integer> cf2 = cf1.handle((result, e) -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        System.out.println("上个任务结果:" + result);
        System.out.println("上个任务抛出异常:" + e);
        if (e != null) {
            // 处理异常情况,返回默认值或抛出异常
            return null;
        } else {
            // 正常处理结果
            return result + 2;
        }
    });

    //等待任务2执行完成
    System.out.println("cf2结果->" + cf2.get());
}

@Test
public void handleAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        int a = 1/0;
        return 1;
    });

    CompletableFuture<Integer> cf2 = cf1.handleAsync((result, e) -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        System.out.println("上个任务结果:" + result);
        System.out.println("上个任务抛出异常:" + e);
        if (e != null) {
            // 处理异常情况,返回默认值或抛出异常
            return null;
        } else {
            // 正常处理结果
            return result + 2;
        }
    });

    //等待任务2执行完成
    System.out.println("cf2结果->" + cf2.get());
}

3、多任务组合处理

3.1 thenCombine、thenAcceptBoth 和runAfterBoth

这三个方法都是将两个CompletableFuture组合起来处理,只有两个任务都正常完成时,才进行下阶段任务。

区别:thenCombine会将两个任务的执行结果作为所提供函数的参数,且该方法有返回值;thenAcceptBoth同样将两个任务的执行结果作为方法入参,但是无返回值;runAfterBoth没有入参,也没有返回值。注意两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。所以需要注意对于异常的处理。

@Test
public void thenCombine() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        return 2;
    });

    CompletableFuture<Integer> cf3 = cf1.thenCombine(cf2, (a, b) -> {
        System.out.println(Thread.currentThread() + " cf3 do something....");
        return a + b;
    });

    System.out.println("cf3结果->" + cf3.get());

}

@Test
public void thenAcceptBoth() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        return 2;
    });

    CompletableFuture<Void> cf3 = cf1.thenAcceptBoth(cf2, (a, b) -> {
        System.out.println(Thread.currentThread() + " cf3 do something....");
        System.out.println(a + b);
    });

    System.out.println("cf3结果->" + cf3.get());
}

@Test
public void runAfterBoth() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        return 2;
    });

    CompletableFuture<Void> cf3 = cf1.runAfterBoth(cf2, () -> {
        System.out.println(Thread.currentThread() + " cf3 do something....");
    });

    System.out.println("cf3结果->" + cf3.get());
}

3.2 applyToEither、acceptEither和runAfterEither

这三个方法和上面一样也是将两个CompletableFuture组合起来处理,当有一个任务正常完成时,就会进行下阶段任务。

区别:applyToEither会将已经完成任务的执行结果作为所提供函数的参数,且该方法有返回值;acceptEither同样将已经完成任务的执行结果作为方法入参,但是无返回值;runAfterEither没有入参,也没有返回值。

@Test
public void applyToEither() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        return 2;
    });

    CompletableFuture<Integer> cf3 = cf1.applyToEither(cf2, (a) -> {
        System.out.println(Thread.currentThread() + " cf3 do something....");
        return a;
    });

    System.out.println("cf3结果->" + cf3.get());
}
@Test
public void acceptEither() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        return 2;
    });

    CompletableFuture<Void> cf3 = cf1.acceptEither(cf2, (a) -> {
        System.out.println(Thread.currentThread() + " cf3 do something....");
    });

    System.out.println("cf3结果->" + cf3.get());
}

@Test
public void runAfterEither() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        return 1;
    });

    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        return 2;
    });

    CompletableFuture<Void> cf3 = cf1.runAfterEither(cf2, () -> {
        System.out.println(Thread.currentThread() + " cf3 do something....");
    });

    System.out.println("cf3结果->" + cf3.get());
}

3.3 allOf / anyOf

allOf:当使用 allOf 方法时,如果所有的任务都成功完成,那么返回的 CompletableFuture 在调用 get 方法时不会返回任何结果(因为 allOf 返回的 CompletableFuture 的类型是 Void),而只是简单地阻塞直到所有任务完成。但是,如果任何一个任务在执行过程中抛出了异常,那么当调用 get 方法时,会抛出 CompletionException,这个异常封装了原始任务中抛出的异常。

anyOf :当你调用 get 方法时,如果任何一个任务已经成功完成,get 方法会返回那个已经完成的任务的结果。但是,如果任何一个任务是以异常结束的,get 方法在调用时会抛出 CompletionException,这个异常封装了原始任务中抛出的异常。

/**
 * anyOf和allOf 方法用于接受一个 CompletableFuture 对象的数组或集合,并返回一个新的 CompletableFuture 对象
 * 区别:
 * anyOf:当任意一个 CompletableFuture 完成时,返回的 CompletableFuture 就会完成。
 * allOf:只有当所有的 CompletableFuture 都完成时,返回的 CompletableFuture 才会完成。
 */
@Test
public void testAllOf() throws ExecutionException, InterruptedException {
    CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
        try {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("cf1 任务完成");
        return "cf1 任务完成";
    });

    CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
        try {
            System.out.println(Thread.currentThread() + " cf2 do something....");
        //    int a = 1/0;
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("cf2 任务完成");
        return "cf2 任务完成";
    });

    CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
        try {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("cf3 任务完成");
        return "cf3 任务完成";
    });

    CompletableFuture<Void> cfAll = CompletableFuture.allOf(cf1, cf2, cf3);
    System.out.println("cfAll结果->" + cfAll.get());
}

@Test
public void testAnyOf() throws ExecutionException, InterruptedException {
    CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
        try {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("cf1 任务完成");
        return "cf1 任务完成";
    });

    CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
        try {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("cf2 任务完成");
        return "cf2 任务完成";
    });

    CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
        try {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("cf3 任务完成");
        return "cf3 任务完成";
    });

    CompletableFuture<Object> any = CompletableFuture.anyOf(cf1, cf2, cf3);
    System.out.println("cfAny结果->" + any.get());
}

4、异常处理

4.1 whenComplete/handle 方法

CompletableFuture 在 Java 中提供了一些非常灵活的方式来处理异步编程中的异常。它提供了几种处理异常的方式,包括使用 exceptionally、whenComplete和handle方法。在2.4和2.5的案例中展示了使用两种方法处理异常的情况,参考可以详见2.4和2.5。

4.2 exceptionally方法

exceptionally方法也是一个用于处理异常情况的非常有用的工具。它允许你为CompletableFuture链式调用中发生的异常提供一个回退逻辑。当CompletableFuture的计算结果或链式调用中的某个步骤抛出异常时,你可以使用exceptionally来提供一个备用的结果或进行异常处理。

exceptionally方法接收一个Function<Throwable, ? extends U>类型的参数,该函数接受一个Throwable对象(即异常)并返回一个结果。当CompletableFuture的计算完成时(无论是正常完成还是异常完成),exceptionally中提供的函数只会在发生异常时被调用。
以上述2.5中的代码示例做相关修改,不采用在cf2中判断是否有异常,而是在cf1中对异常给出一个默认返回结果。

@Test
public void handleAsync() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread() + " cf1 do something....");
        int a = 1/0;
        return 1;
    }).exceptionally((throwable)->{
        System.out.println(throwable.getMessage());
        return 0;
    });

    CompletableFuture<Integer> cf2 = cf1.handleAsync((result, e) -> {
        System.out.println(Thread.currentThread() + " cf2 do something....");
        System.out.println("上个任务结果:" + result);
        System.out.println("上个任务抛出异常:" + e);
        return result + 2;
    });

    //等待任务2执行完成
    System.out.println("cf2结果->" + cf2.get());
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_39106990/article/details/138314357

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值