目录
CompleteableFuture
创建
获得结果的方法
辅助方法
allOf和anyOf的区别
CompletableFuture 里大约有五十种方法,但是可以进行归类:
变换类 thenApply
消费类 thenAccept
执行操作类 thenRun
thenApply/thenAccept/thenRun
结合转化类 thenCombine
结合转化类 thenCompose
结合消费类 thenAcceptBoth
运行后执行类 runAfterBoth
取最快转换类 applyToEither
取最快消费类 acceptEither
取最快运行后执行类 runAfterEither
异常补偿类 exceptionally
运行后记录结果类 whenComplete
运行后处理结果类 handle
CompleteableFuture
Java 的 1.5 版本引入了 Future,可以把它简单的理解为运算结果的占位符,它提供了两个方法来获取运算结果。
get():调用该方法线程将会无限期等待运算结果。
get(long timeout, TimeUnit unit):调用该方法线程将仅在指定时间 timeout 内等待结果,如果等待超时就会抛出 TimeoutException 异常。
Future 可以使用 Runnable 或 Callable 实例来完成提交的任务,它存在如下几个问题:
- 阻塞调用 get() 方法会一直阻塞,直到等待直到计算完成,它没有提供任何方法可以在完成时通知,同时也不具有附加回调函数的功能。
- 链式调用和结果聚合处理在很多时候我们想链接多个 Future 来完成耗时较长的计算,此时需要合并结果并将结果发送到另一个任务中,该接口很难完成这种处理。
- 异常处理 Future 没有提供任何异常处理的方式。
JDK1.8 才新加入的一个实现类 CompletableFuture,很好的解决了这些问题,CompletableFuture 实现了 Future<T>, CompletionStage<T>两个接口。实现了Future 接口,意味着可以像以前一样通过阻塞或者轮询的方式获得结果。
Future一次只能执行一个任务,拿到一次结果,CompletableFuture处理链式任务,组合任务
创建
除了直接 new 出一个 CompletableFuture 的实例,还可以通过工厂方法创建 CompletableFuture 的实例
工厂方法:
Asynsc 表示异步,而 supplyAsync 与 runAsync 不同在于,supplyAsync 异步返 回一个结果,runAsync 是 void。第二个函数第二个参数表示是用我们自己创建的线程池,否则采用默认的 ForkJoinPool.commonPool()作为它的线程池。
获得结果的方法
public T get()
public T get(long timeout, TimeUnit unit)
public T getNow(T valueIfAbsent)
public T join()
getNow 有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的 valueIfAbsent 值。
join 返回计算的结果或者抛出一个 unchecked 异常(CompletionException),它和 get 对抛出的异常的处理有些细微的区别。
辅助方法
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
return orTree(cfs, 0, cfs.length - 1);
}
allOf和anyOf的区别
// 类说明:allOf和anyOf的区别
public class AllofAnyOf {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Random rand = new Random();
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future1完成");
return 100;
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future2完成");
return "abc";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future3完成");
return "123abc";
});
/*allOf会等待全部异步方法(future1,future2,future3)执行完后,执行 thenRun() 中的操作
只要有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null*/
CompletableFuture.allOf(future1,future2,future3).thenRun(()->{
System.out.println("All done!");
});
/*anyOf方法会等待其中任一任务完成后,返回一个最先完成任务的CompletableFuture对象,
通过 get()方法获取完成任务的结果。如果该任务执行异常,则抛出异常*/
CompletableFuture<Object> f = CompletableFuture.anyOf(future1,future2,future3);
System.out.println("f>>" + f.get());
SleepTools.second(5);//使主线程休眠 5 秒,以确保异步任务有足够的时间执行
}
}
运行结果:
allOf 方法是当所有的 CompletableFuture 都执行完后执行计算。
anyOf 方法是当任意一个 CompletableFuture 执行完后就会执行计算,计算的结果相同。
CompletionStage 是一个接口,从命名上看得知是一个完成的阶段,它代表了一个特定的计算的阶段,可以同步或者异步的被完成。你可以把它看成一个计算流水线上的一个单元,并最终会产生一个最终结果,这意味着几个CompletionStage 可以串联起来,一个完成的阶段可以触发下一阶段的执行,接着触发下一次,再接着触发下次,….。
总结 CompletableFuture 几个关键点:
1、计算可以由 Future ,Consumer 或者 Runnable 接口中的 apply,accept 或者 run 等方法表示。
2、计算的执行主要有以下
- 默认执行
- 使用默认的 CompletionStage 的异步执行提供者异步执行。这些方法名使用 someActionAsync 这种格式表示。
- 使用 Executor 提供者异步执行。这些方法同样也是 someActionAsync 这种格式,但是会增加一个 Executor 参数。
CompletableFuture 里大约有五十种方法,但是可以进行归类:
变换类 thenApply
public class ThenApplyExample {
public static void main(String[] args) {
//thenApply() 方法是一个同步方法,它在当前线程执行,
//它接受一个 Function 参数,用于对前一个阶段(即前一个 CompletableFuture 对象)的结果进行转换。
//通过 join() 方法等待异步任务完成
String result = CompletableFuture.supplyAsync(() -> "hello").thenApply(s -> s + " world").join();
System.out.println(result);//hello world
//thenApplyAsync() 方法是一个异步方法,它会在一个新的线程中执行。
//它也接受一个 Function 参数,用于对前一个阶段(前一个 CompletableFuture 对象)的结果进行转换。
//通过 join() 方法等待异步任务完成
String result2 = CompletableFuture.supplyAsync(() -> "hello2").thenApplyAsync(s -> s + " world2").join();
System.out.println(result2);//hello2 world2
}
}
关键入参是函数式接口 Function。它的入参是上一个阶段计算后的结果,返回值是经过转化后结果。
thenApply() 方法是一个同步方法,它在当前线程执行。
thenApplyAsync() 方法是一个异步方法,它会在一个新的线程中执行。是异步执行的,它不会阻塞当前线程,允许多个任务并行执行,提高了任务处理的效率。
public class ThenApplyExample2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建一个ForkJoinPool线程池
ForkJoinPool pool= new ForkJoinPool();
// 使用CompletableFuture的supplyAsync方法,提交一个异步任务到ForkJoinPool线程池中执行
CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(()->{
// 打印当前线程的信息和任务开始标志
System.out.println(Thread.currentThread()+" cf start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace(); // 捕获并打印中断异常
}
System.out.println(Thread.currentThread()+" cf end");
return 100;// 返回计算结果
},pool);
// thenApply方法会在cf异步任务完成后执行,cf任务的返回结果将作为thenApply方法的输入参数
// thenApply会创建一个新的CompletableFuture实例
CompletableFuture<String> cf2 =cf.thenApply((result)->{
System.out.println(Thread.currentThread()+" cf2 start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+" cf2 end");
return "test:"+result;
});
// 调用cf的get方法,等待cf任务完成并获取其返回结果
System.out.println("run result->"+cf.get());
System.out.println("run result->"+cf2.get());
}
}
运行结果:
将ThenApplyExample2的thenApply方法改成thenApplyAsync后,运行结果如下:
thenApplyAsync默认使用ForkJoinPool.commonPool(),也可以指定执行异步任务的Executor实现。 下面列举的每个方法都有两个以Async结尾的方法,一个使用默认的Executor实现,一个使用指定的Executor实现,不带Async的方法是由触发该任务的线程执行该任务,带Async的方法是由触发该任务的线程将任务提交到线程池,执行任务的线程跟触发任务的线程不一定是同一个 。
消费类 thenAccept
public class ThenAcceptAsyncExample {
public static void main(String[] args) {
// 创建一个异步任务,返回字符串 "hello"
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "hello");
// 使用 thenAccept 方法消费前一个阶段的结果,并输出结果
future1.thenAccept(s -> System.out.println(s + " world")); // 输出:hello world
// 创建一个异步任务,返回字符串 "hello2"
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "hello2");
// 使用 thenAcceptAsync 方法异步消费前一个阶段的结果,并输出结果
future2.thenAcceptAsync(s -> System.out.println(s + " world2")); // 输出:hello2 world2
}
}
关键入参是函数式接口 Consumer。它的入参是上一个阶段计算后的结果, 没有返回值。
thenAccept() 方法是一个同步方法,它在当前线程执行。它接受一个 Consumer 参数,用于消费前一个阶段(前一个 CompletableFuture 对象)的结果。
thenAcceptAsync() 方法是一个异步方法,它会在一个新的线程中执行。它接受一个 Consumer 参数,用于消费前一个阶段(前一个 CompletableFuture 对象)的结果。这样的异步处理允许多个任务并行执行,提高了任务处理的效率。
执行操作类 thenRun
public class ThenRunAsyncExample {
public static void main(String[] args) {
// 创建一个异步任务,休眠 1 秒后返回字符串 "hello"
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "hello";
}).thenRun(() -> System.out.println("hello world")); // 输出:hello world
// 创建一个异步任务,休眠 1 秒后返回字符串 "hello2"
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "hello2";
}).thenRunAsync(() -> System.out.println("hello world2")); // 输出:hello world2
// 等待所有异步任务完成
SleepTools.second(2);
}
}
对上一步的计算结果不关心,执行下一个操作,入参是一个 Runnable 的实例,表示上一步完成后执行的操作。
thenRun() 方法和 thenRunAsync() 方法都用于执行一些操作,但不返回任何结果。它们主要用于链式调用时,处理一些与结果无关的逻辑。
thenRun() 方法是同步方法,在当前线程执行。thenRunAsync() 方法是异步方法,会在新的线程中执行,允许多个任务并行执行。
thenApply/thenAccept/thenRun
public class ThenExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建一个ForkJoinPool线程池
ForkJoinPool pool=new ForkJoinPool();
// 使用CompletableFuture的supplyAsync方法,提交一个异步任务到ForkJoinPool线程池中执行
CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread()+" cf start ");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+" cf end ");
return 100;
},pool);
// thenApply方法:cf任务完成后执行,cf任务的返回结果作为thenApply方法的输入参数
CompletableFuture cf2 = cf.thenApply((result)->{
System.out.println(Thread.currentThread()+" cf2 thenApply start ");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+" cf2 thenApply end");
return "test:"+result;
}).thenAccept((result)-> { // thenAccept方法:cf2任务完成后执行,接收cf2的结果作为输入参数,但没有返回值
System.out.println(Thread.currentThread()+" cf3 thenAccept start ");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cf2-2 "+ result);
System.out.println(Thread.currentThread()+" cf3 thenAccept end");
}).thenRun(()->{ // thenRun方法:cf3任务完成后执行,无输入参数,也没有返回值
System.out.println(Thread.currentThread()+" cf4 thenRun start ");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thenRun...");
System.out.println(Thread.currentThread()+" cf4 thenRun end");
});
//等待子任务执行完成
System.out.println("cf->"+cf.get());
//cf2 等待最后一个thenRun执行完成
System.out.println("cf2->"+cf2.get());
}
}
运行结果:
结合转化类 thenCombine
public class ThenCombineAsyncExample {
public static void main(String[] args) {
// 创建两个异步任务
//thenCombine() 方法是一个同步方法,它在当前线程执行。
//它接受两个参数:第一个参数是另一个 CompletableFuture 对象,第二个参数是一个 BiFunction
//用于对两个 CompletableFuture 对象的结果进行组合。并通过 BiFunction 将它们拼接成 "hello world"。
String result = CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "hello";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "world";
}), (s1, s2) -> s1 + " " + s2).join();
System.out.println(result); // 输出:hello world
// 创建两个异步任务
String result2 = CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "hello2";
}).thenCombineAsync(CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "world2";
}), (s1, s2) -> s1 + " " + s2).join();
System.out.println(result2); // 输出:hello2 world2
}
}
需要上一步的处理返回值,并且 other 代表的 CompletionStage 有返回值之后,利用这两个返回值,进行转换后返回指定类型的值。
两个 CompletionStage 是并行执行的,它们之间并没有先后依赖顺序,other 并不会等待先前的 CompletableFuture 执行完毕后再执行。
它接受两个参数:第一个参数是另一个 CompletableFuture 对象,第二个参数是一个 BiFunction 用于对两个 CompletableFuture 对象的结果进行组合。
thenCombine() 方法是同步方法,在当前线程执行。thenCombineAsync() 方法是异步方法,会在新的线程中执行,允许多个任务并行执行。
thenCombine() 方法和 thenCombineAsync() 方法返回一个新的 CompletableFuture 对象,表示两个异步任务的组合结果。您可以通过 get() 方法或 join() 方法等待组合后的结果,并对其进行后续操作。
thenCombine() 和 thenCombineAsync() 方法允许您在两个异步任务的结果上执行进一步的操作,并组合它们的结果,以实现更复杂的异步任务处理逻辑。
//也可以这样写
public class ThenCombineExample2 {
public static void main(String[] args) {
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
System.out.println("future3");
return "hello";
});
CompletableFuture<String> future4 = CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
System.out.println("future4");
return "word";
});
CompletableFuture<String> future5 = future3.thenCombine(future4, (s1, s2)->s1 + s2);
System.out.println("result: " + future5.join());
}
}
//运行结果
future3
future4
result: helloword
结合转化类 thenCompose
public class ThenComposeAsyncExample {
public static void main(String[] args) {
// 创建一个异步任务
//thenCompose() 方法是一个同步方法,它在当前线程执行。
//它接受一个 Function 参数,该函数对前一个阶段(即前一个 CompletableFuture 对象)的结果进行处理,并返回一个新的 CompletableFuture 对象。
//可以通过 get() 方法或 join() 方法等待组合后的结果,并对其进行后续操作。
Integer result = CompletableFuture.supplyAsync(() -> {
return 10;
}).thenCompose(i -> CompletableFuture.supplyAsync(() -> { return i + 1; })).join();
System.out.println(result); // 输出:11
// 创建一个异步任务
// thenComposeAsync() 方法是一个异步方法,它会在一个新的线程中执行。
Integer result2 = CompletableFuture.supplyAsync(() -> {
return 100;
}).thenComposeAsync(i -> CompletableFuture.supplyAsync(() -> { return i + 2; })).join();
System.out.println(result2); // 输出:102
}
}
对于 Compose 可以连接两个 CompletableFuture,其内部处理逻辑是当第一个 CompletableFuture 处理没有完成时会合并成一个 CompletableFuture,如果处理完成,第二个 future 会紧接上一个 CompletableFuture 进行处理。 第一个 CompletableFuture 的处理结果是第二个 future 需要的输入参数。
结合消费类 thenAcceptBoth
public class ThenAcceptBothAsyncExample {
public static void main(String[] args) {
// 创建两个异步任务
//thenAcceptBoth() 方法是一个同步方法,它在当前线程执行。
//它接受两个参数:第一个参数是另一个 CompletableFuture 对象,第二个参数是一个 BiConsumer,用于消费这两个 CompletableFuture 对象的结果。
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "hello";
}).thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "world";
}), (s1, s2) -> System.out.println(s1 + " " + s2)); // 输出:hello world
// 等待一段时间,确保组合任务有足够时间完成
SleepTools.second(3); // 注意:这里需要足够长的等待时间,以确保组合任务完成输出结果
// 创建两个异步任务
// thenAcceptBothAsync() 方法是一个异步方法,它会在一个新的线程中执行。
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "hello2";
}).thenAcceptBothAsync(CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "world2";
}), (s1, s2) -> System.out.println(s1 + " " + s2)); // 输出:102
// 等待一段时间,确保组合任务有足够时间完成
SleepTools.second(3);
}
}
需要上一步的处理返回值,并且 other 代表的 CompletionStage 有返回值之后,利用这两个返回值,进行消费,允许您在两个异步任务的结果上执行消费操作,实现多个任务的组合处理。
运行后执行类 runAfterBoth
public class RunAfterBothAsyncExample {
public static void main(String[] args) {
// 创建两个异步任务
// runAfterBoth() 方法是一个同步方法,它在当前线程执行。
// 它接受两个参数:第一个参数是另一个 CompletableFuture 对象,
// 第二个参数是一个 Runnable,用于指定在两个 CompletableFuture 对象都完成后需要执行的任务。
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "s1";
}).runAfterBoth(CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "s2";
}), () -> System.out.println("hello world1")); // 输出:hello world1
// 创建两个异步任务,休眠 1 秒和 2 秒后分别返回字符串 "s3" 和 "s4"
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "s3";
}).runAfterBothAsync(CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "s4";
}), () -> System.out.println("hello world2")); // 输出:hello world2
// 等待一段时间,确保异步任务有足够时间完成
SleepTools.second(3); // 注意:这里需要足够长的等待时间,以确保异步任务完成输出结果
}
}
不关心这两个 CompletionStage 的结果,只关心这两个 CompletionStage 都执行完毕,之后再进行操作(Runnable)。
runAfterBoth() 方法和 runAfterBothAsync() 方法用于在两个异步任务都完成后执行一个 Runnable 任务,适用于需要在两个异步任务都完成后进行后续操作的场景。
取最快转换类 applyToEither
public class ApplyToEitherAsyncExample {
public static void main(String[] args) {
// 创建两个异步任务
// applyToEither() 方法是一个同步方法,它在当前线程执行。
// 它接受两个参数:第一个参数是另一个 CompletableFuture 对象,
// 第二个参数是一个 Function,用于对第一个完成的 CompletableFuture 对象的结果进行处理。
// 在两个任务中的任意一个完成后,将其结果应用到一个函数,并返回最先完成任务的结果。
String result = CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "S1";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "S2";
}), s -> s).join();
System.out.println(result); // 输出:S1 或 S2,取决于哪个任务先完成
// 创建两个异步任务
String result2 = CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "S3";
}).applyToEitherAsync(CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "S4";
}), s -> s).join();
System.out.println(result2); // 输出:S3 或 S4,取决于哪个任务先完成
}
}
两个 CompletionStage,谁计算的快,我就用那个 CompletionStage 的结果进行下一步的转化操作。现实开发场景中,总会碰到有两种渠道完成同一个事情, 所以就可以调用这个方法,找一个最快的结果进行处理。
取最快消费类 acceptEither
public class AcceptEitherAsyncExample {
public static void main(String[] args) {
// 创建两个异步任务
// acceptEither() 方法是一个同步方法,它在当前线程执行。
// 它接受两个参数:第一个参数是另一个 CompletableFuture 对象,
// 第二个参数是一个 Consumer,用于对第一个完成的 CompletableFuture 对象的结果进行消费操作。
// 在两个任务中的任意一个完成后,执行一个消费操作将其结果输出。
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "S1";
}).acceptEither(CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "S2";
}), (s) -> System.out.println(s)); // 输出:S1 或 S2,取决于哪个任务先完成
// 创建两个异步任务
CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "S3";
}).acceptEitherAsync(CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "S4";
}), (s) -> System.out.println(s)); // 输出:S3 或 S4,取决于哪个任务先完成
// 等待一段时间,确保异步任务有足够时间完成
SleepTools.second(3); // 注意:这里需要足够长的等待时间,以确保异步任务完成输出结果
}
}
两个 CompletionStage,谁计算的快,我就用那个 CompletionStage 的结果进行下一步的消费操作。
acceptEither() 和 acceptEitherAsync() 方法允许您在两个异步任务中的任意一个完成后,执行一个消费操作,适用于需要对最先完成任务结果进行处理的场景。
取最快运行后执行类 runAfterEither
public class RunAfterEitherExample {
public static void main(String[] args) {
// 创建两个异步任务
// runAfterEither() 方法是一个同步方法,它在当前线程执行。
// 它接受两个参数:第一个参数是另一个 CompletableFuture 对象,
// 第二个参数是一个 Runnable,用于指定在任意一个 CompletableFuture 对象完成后需要执行的任务。
// 指定在这两个任务中的任意一个完成后输出 "hello world"。
CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "S1";
}).runAfterEither(CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "S2";
}), () -> System.out.println("hello world")); // 输出:"hello world",任何一个任务完成都会输出
// 创建两个异步任务
CompletableFuture.supplyAsync(() -> {
SleepTools.second(2);
return "S3";
}).runAfterEither(CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "S4";
}), () -> System.out.println("hello world2")); // 输出:"hello world2",任何一个任务完成都会输出
// 等待一段时间,确保异步任务有足够时间完成
SleepTools.second(3); // 注意:这里需要足够长的等待时间,以确保异步任务完成输出结果
}
}
两个 CompletionStage,任何一个完成了都会执行下一步的操作(Runnable)。 适用于需要在任意一个任务完成后进行后续操作的场景。
异常补偿类 exceptionally
public class ExceptionallyExample {
public static void main(String[] args) {
// 创建一个异步任务
// exceptionally() 方法是一个同步方法,它在当前线程执行。
// 它接受一个 Function 参数,用于处理异步任务发生的异常。
// 如果异步任务发生异常,exceptionally() 方法会返回一个新的 CompletableFuture 对象,该对象会返回由处理函数提供的默认值。
// 因为任务发生了异常,exceptionally() 方法会捕获该异常,并返回一个新的 CompletableFuture 对象,该对象的结果由处理函数 "hello world" 提供。
String result = CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
if (1 == 1) {
throw new RuntimeException("测试一下异常情况");
}
return "S1";
}).exceptionally(e -> {
System.out.println(e.getMessage()); // 输出:"测试一下异常情况"
return "hello world";
}).join();
// 输出:"hello world",因为发生异常,所以返回默认值 "hello world",未发生异常输出"S1"
System.out.println(result);
}
}
当运行时出现了异常,可以通过 exceptionally 进行补偿。
在使用 exceptionally() 方法时,如果异步任务没有发生异常,则会返回原始结果;如果发生了异常,则会返回处理函数提供的默认值。
exceptionally() 方法允许您在异步任务发生异常时进行恢复操作,返回一个默认值或其他处理结果,适用于需要对异步任务的异常进行处理的场景。
运行后记录结果类 whenComplete
public class WhenCompleteAndExceptionallyExample {
public static void main(String[] args) {
// 创建一个异步任务
// whenComplete()方法是一个同步方法,它在当前线程执行。它接受一个 BiConsumer 参数,用于在异步任务完成或发生异常时进行处理。
// BiConsumer 接收两个参数:第一个参数是异步任务的结果(如果成功完成),第二个参数是异步任务抛出的异常(如果发生异常)。
// whenComplete() 方法会捕获该异常,并执行处理函数打印异常信息。
// exceptionally() 方法是一个同步方法,它在当前线程执行。它接受一个 Function 参数,用于处理异步任务发生的异常。
// 如果异步任务发生异常,exceptionally() 方法会捕获该异常,并返回一个新的 CompletableFuture 对象,该对象会返回由处理函数提供的默认值。
// 通过 exceptionally() 方法在任务发生异常后,捕获该异常并返回一个新的 CompletableFuture 对象,该对象的结果由处理函数 "hello world" 提供。
String result = CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
if (1 == 1) {
throw new RuntimeException("测试一下异常情况");
}
return "s1";
}).whenComplete((s, t) -> {
System.out.println(s); // 输出null,因为发生了异常
System.out.println(t.getMessage());
}).exceptionally(e -> {
System.out.println(e.getMessage());
return "hello world";
}).join();
System.out.println(result); // 输出:"hello world",因为发生异常,所以返回默认值 "hello world"
}
}
//运行结果
null
java.lang.RuntimeException: 测试一下异常情况
java.lang.RuntimeException: 测试一下异常情况
hello world
//注释if (1 == 1) {throw new RuntimeException("测试一下异常情况");}
s1
java.lang.NullPointerException
hello world
执行完毕后它的结果返回原始的 CompletableFuture 的计算结果或者返回异常。所以不会对结果产生任何的作用。
whenComplete() 方法用于在异步任务完成或发生异常时进行处理,不影响最终的返回结果,它可以用来记录日志、清理资源等,不改变原始计算的结果或异常。
exceptionally() 方法用于在异步任务发生异常时进行处理,返回一个默认值或其他处理结果,用于恢复异常情况。
通过结合使用 whenComplete() 和 exceptionally() 方法,可以在处理异步任务时对任务的结果和异常进行灵活处理。适用于需要对异步任务的结果或异常进行特定操作的场景,例如,可以在 whenComplete 中记录日志、清理资源,并在 exceptionally 中返回默认值以恢复任务的结果。
运行后处理结果类 handle
public class HandleExample {
public static void main(String[] args) {
// 创建一个异步任务,休眠 1 秒后抛出异常
// handle() 方法是一个同步方法,它在当前线程执行。它接受一个 BiFunction 参数,用于处理异步任务的结果或异常。
// BiFunction 接收两个参数:第一个参数是异步任务的结果(如果成功完成),第二个参数是异步任务抛出的异常(如果发生异常)。
// 发生了异常 handle() 方法会捕获该异常,并返回一个新的结果 "hello world",用于处理异常情况。
String result = CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
// 出现异常
if (1 == 1) {
throw new RuntimeException("测试一下异常情况");
}
return "s1";
}).handle((s, t) -> { // 出现异常 s=null, t=java.util.concurrent.CompletionException: java.lang.RuntimeException: 测试一下异常情况
if (t != null) {
return "hello world"; // 处理异常情况,返回 "hello world"
}
return s; // 返回原始结果 "s1",因为没有异常发生
}).join();
System.out.println(result); // 输出:"hello world",因为发生异常,返回处理后的结果 "hello world"
// 创建一个异步任务,休眠 1 秒后返回结果 "s1"
String result2 = CompletableFuture.supplyAsync(() -> {
SleepTools.second(1);
return "s1";
}).handle((s, t) -> { // 未出现异常 s="s1" , t=null
if (t != null) {
return "hello world"; // 处理异常情况,返回 "hello world"
}
return s; // 返回原始结果 "s1",因为没有异常发生
}).join();
System.out.println(result2); // 输出:"s1",因为没有发生异常,返回原始结果 "s1"
}
}
运行完成时,对结果的处理。这里的完成时有两种情况,一种是正常执行, 返回值。另外一种是遇到异常抛出造成程序的中断 。
handle() 方法用于在异步任务完成或发生异常时进行处理,可以根据是否发生异常来返回不同的结果。适用于需要对异步任务的结果或异常进行特定处理的场景。