Java并发/多线程CompleteableFuture详解

目录

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=nullif (t != null) {return "hello world"; // 处理异常情况,返回 "hello world"}return s; // 返回原始结果 "s1",因为没有异常发生}).join();System.out.println(result2); // 输出:"s1",因为没有发生异常,返回原始结果 "s1"}
}

运行完成时,对结果的处理。这里的完成时有两种情况,一种是正常执行, 返回值。另外一种是遇到异常抛出造成程序的中断 。

handle() 方法用于在异步任务完成或发生异常时进行处理,可以根据是否发生异常来返回不同的结果。适用于需要对异步任务的结果或异常进行特定处理的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/868903.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何成功的设计BGA?

目前&#xff0c;用于容纳各种先进多功能半导体器件&#xff08;如 FPGA 和微处理器&#xff09;的标准封装是球栅阵列 &#xff08;BGA&#xff09;。BGA 封装中的组件用于各种嵌入式设计中&#xff0c;既可用作主机处理器&#xff0c;也可用作存储器等外设。多年来&#xff0…

硅纪元小课堂 | 重燃回忆!手把手教你用AI免费让老照片“活”起来!

在这个数字化的时代&#xff0c;我们的照片承载了无数珍贵的回忆。然而&#xff0c;随着时间的流逝&#xff0c;这些照片可能会褪色、损坏&#xff0c;甚至丢失。但现在&#xff0c;有了AI技术的加持&#xff0c;我们可以让这些老照片重新焕发生机&#xff0c;甚至让它们“活”…

图论·Day01

P3371 P4779 P3371 【模板】单源最短路径&#xff08;弱化版&#xff09; 注意的点&#xff1a; 边有重复&#xff0c;选择最小边&#xff01;对于SPFA算法容易出现重大BUG&#xff0c;没有负权值的边时不要使用&#xff01;&#xff01;&#xff01; 70分代码 朴素板dijsk…

《人生苦短,我用python·八》多线程和线程池的使用

在Python中&#xff0c;多线程是一种并发编程技术&#xff0c;它允许多个线程在程序中并行执行。这对于IO密集型任务&#xff08;如网络请求、文件读写等&#xff09;非常有用&#xff0c;因为这些任务在等待外部资源时可以让其他线程继续工作&#xff0c;从而提高程序的效率。…

openssl交叉编译-移植ARM

OpenSSL是一个开源的密码学工具包&#xff0c;提供了一组用于网络安全的加密和解密算法、协议以及相关工具的库&#xff0c;它通过提供多种加密算法、协议和工具&#xff0c;为网络通信和数据存储提供了强大的安全保障。 主要功能 加密和解密&#xff1a; OpenSSL提供了多种对…

CDGA|数据治理:构建高质量数据要素供给体系的核心在于畅通流通渠道

随着数字化时代的到来&#xff0c;数据已经成为驱动经济社会发展的核心要素。数据治理作为确保数据质量、保障数据安全、促进数据价值实现的重要手段&#xff0c;其重要性日益凸显。在数据治理的众多环节中&#xff0c;构建高质量数据要素供给体系尤为关键&#xff0c;而该体系…

C基础day6

1、思维导图 2、 #include<myhead.h> #define MAX 10 int main(int argc, const char *argv[]) {//定义一个数组&#xff0c;用于存储班级所有成员的成绩int score[MAX] {0};//完成对成员成绩的输入for(int i0;i<MAX;i){//任意一个元素score[i]printf("请输入第…

springboot学生成绩管理系统-计算机毕业设计源码49296

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

假期笔记1:anaconda的安装与pycharm中的引用

1.下载安装 Download Anaconda Distribution | Anaconda 2.填个邮箱 11111.. 3.下载。有点需要时间 4.安装&#xff0c;双击&#xff0c;根据实际进行&#xff0c;记清安装路径 5。环境设置 conda -V 6.创建环境 conda create --name env_name conda create --na…

利用JavaScript在HTML页面搜索并高亮匹配的文本

网页中内容比较多的话&#xff0c;有时候需要通过搜索快速查找特定的文本。当然&#xff0c;这可以通过浏览器的搜索功能实现&#xff0c;但是&#xff0c;象Anki的复习界面这样的场景&#xff0c;并没有搜索功能&#xff0c;我们就需要自己在网页上提供一个搜索框来实现。 下…

金蝶API取数+JSON解析,FDL助力高效数据处理

目录 一、企业介绍 二、业务难题与挑战 商管预算管理瓶颈凸显&#xff1a;金蝶数据手工导出&#xff0c;跨库关联分析时效受限 金蝶API数据提取&#xff1a;挑战重重的技术攻坚战 三、解决方案 商管预算管理升级&#xff1a;API取数JSON解析&#xff0c;FineDataLink助力高效数…

18.按键消抖模块设计(使用状态机,独热码编码)

&#xff08;1&#xff09;设计意义&#xff1a;按键消抖主要针对的时机械弹性开关&#xff0c;当机械触点断开、闭合时&#xff0c;由于机械触点的弹性作用&#xff0c;一个按键开关在闭合时不会马上稳定地接通&#xff0c;在断开时也不会一下子就断开。因而在闭合以及断开的瞬…

PHP微票务微信小程序系统源码

&#x1f39f;️【一键购票&#xff0c;便捷生活新体验】微票务系统小程序全解析 &#x1f4f1;【随时随地&#xff0c;票务尽在掌握】 告别排队购票的烦恼&#xff0c;微票务系统小程序让你随时随地轻松购票&#xff01;无论是热门演唱会的门票、热门景点的入园券&#xff0…

实时追踪与分析用户反馈:淘宝/天猫商品评论API的应用实践

实时追踪与分析用户反馈是电商平台提升用户体验、优化产品策略的重要手段。淘宝/天猫作为国内领先的电商平台&#xff0c;其商品评论API接口为商家提供了强大的数据支持&#xff0c;帮助商家实时追踪用户反馈并进行深入分析。以下是淘宝/天猫商品评论API在实时追踪与分析用户反…

推荐一款Win11主题WPF UI框架

最近在微软商店&#xff0c;官方上架了新款Win11风格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,这款应用引入了前沿的Fluent Design UI设计&#xff0c;为用户带来全新的视觉体验。 WPF Gallery简介 做为一关注前沿资讯的开发人员&#xff0c;首先关注的是应用WPF Gallery…

HTML(27)——渐变

渐变是多个颜色逐渐变化的效果&#xff0c;一般用于设置盒子模型 线性渐变 属性&#xff1a;background-image : linear-gradient( 渐变方向 颜色1 终点位置, 颜色2 终点位置, ......&#xff09;&#xff1b; 取值: 渐变方向:可选 to 方位名词角度度数 终点位置:可选 百分…

Java语言+后端+前端Vue,ElementUI 数字化产科管理平台 产科电子病历系统源码

Java语言后端前端Vue,ElementUI 数字化产科管理平台 产科电子病历系统源码 Java开发的数字化产科管理系统&#xff0c;已在多家医院实施&#xff0c;支持直接部署。系统涵盖孕产全程&#xff0c;包括门诊、住院、统计和移动服务&#xff0c;整合高危管理、智能提醒、档案追踪等…

idea运行旧的项目如何引入jar包

背景: 有一个旧项目,年份不详, 生产环境运行正常, 生产环境jenkins打包正常;部分jar包为私包,已无法下载 现在要对这个项目进行调试修改 从生产环境下载正常的jar包解压找到lib把lib放到项目目录中 然后选者对应的jdk版本: 一个模块一个模块的 把刚才的库加进去 然后试着启…

Unity海面效果——5、水沫和海平线

Unity引擎制作海面效果 大家好&#xff0c;我是阿赵。 继续做海面效果&#xff0c;上次做完了漫反射颜色和水波动画&#xff0c;还有法线和高光效果。 原则上来说&#xff0c;这个海面已经基本能看了&#xff0c;从性能的考虑&#xff0c;到这里差不多可以停止了。不过有些细节…

文章SameStr(四):图4代码

“Publication Figure 4” 百度云盘链接: https://pan.baidu.com/s/15g7caZp354zIWktpnWzWhQ 提取码: 4sh7 Libraries Standard Import library(tidyverse) library(cowplot) library(scales) library(ggpubr)Special library(caret) library(plotROC) library(tidymodel…