一、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