[并发编程]异步编程-CompletableFuture

目录

CompletableFuture基本介绍

CompletableFuture继承结构

主要功能

异步执行

supplyAsync(异步执行任务,任务有返回值)

runAsync(异步执行任务,任务没有返回值)

总结 

等待结果

join

get

总结

链式调用

thenAccept(消费结果,无返回值‌)

thenApply

总结

任务组合(组合多个 CompletableFuture)

thenCombine(合并两个独立任务的结果‌)

thenCompose

总结

异常处理

exceptionally

handle

总结


CompletableFuture基本介绍

       CompletableFuture 是 Java 8 引入的一个强大的异步编程工具类,用于支持异步编程非阻塞操作。它提供了一种非阻塞的方式来执行任务,并允许你在任务完成时执行后续操作。相比于传统的 Future 类,CompletableFuture 提供了更多灵活且强大的功能,比如链式调用、任务合并、异常处理等,非常适合处理复杂的异步任务。

CompletableFuture继承结构

        CompletableFuture属于 java.util.concurrent 包的一部分,实现了Future接口CompletionStage接口,前者用于表示异步计算的结果,后者用于表示异步任务之间的依赖关系和组合操作。

        CompletableFuture如果不提供线程池的话,默认使用的ForkJoinPool,依赖于 ForkJoinPool 来执行其异步计算ForkJoinPool内部是守护线程,如果main线程结束了,守护线程会跟着一起结束。

主要功能

异步执行

         可以在后台线程中异步执行任务,而不阻塞主线程。

         supplyAsync 和 runAsync 是 CompletableFuture 类中的两个常用方法,用于在异步线程中执行任务。 

supplyAsync(异步执行任务,任务有返回值)

         

          

         执行有返回值的异步任务(基于 Supplier<U>),返回一个 CompletableFuture<U>‌。

        

         示例1:基本使用

        

        代码说明:

        1.supplyAsync 方法接受一个 lambda 表达式,该表达式模拟了一个耗时的计算(2秒)。

        2.一旦任务完成,结果(42)会被传递到 thenAccept 方法中进行处理。

        3.主线程不会被阻塞,而是继续执行其他工作。

        4.在示例中,主线程在执行完异步任务的处理后,调用 Thread.sleep(3000),确保主线程等待足够的时间,以便异步任务能够完成并打印结果

        5.输出将会包括 Result: 42,并且在等待后,主线程将结束。

        执行结果:

         

         确保在程序中添加了足够的等待时间,以避免主线程提前退出。

        CompletableFuture如果不提供线程池的话,默认使用的ForkJoinPool,而ForkJoinPool内部是守护线程,如果main线程结束了,守护线程会跟着一起结束。

        解释:

        1."Doing other work...":主线程在执行 supplyAsync 方法时,会立即打印这行信息,因为异步任务是在另一个线程中执行的,不会阻塞主线程。

         2."Result: 42":当异步任务完成(即经过约 2 秒后返回结果 42)时,thenAccept 方法被调用,打印出结果。

         3."Main thread finished.":主线程结束。

         执行流程:

  •  主线程开始执行 supplyAsync 方法,启动一个新的异步任务。
  •  主线程继续执行,打印出 "Doing other work..."。
  •  异步任务在后台线程中运行,经过 2 秒后完成并返回结果 42。
  •  一旦异步任务完成,thenAccept 方法接收到结果并打印 "Result: 42"。
  •  主线程结束。

     示例2:自定义线程池的使用

             可以使用自定义线程池来执行异步任务。

             

            使用自定义线程池来可以更好地管理资源,可以更好地控制线程的数量和行为,提高并发任务的管理和执行效率。

              

            执行结果:

            

            执行流程:

            1.主线程启动。

            2.主线程创建线程池。

            3.提交异步任务。

              在主线程中,使用 CompletableFuture.supplyAsync(...) 提交一个异步任务,异步任务则在自定义线程池中的工作线程中执行,与主线程并行运行。

            4.主线程继续执行,输出“主线程继续执行...”的消息。

            5.主线程调用 future.join(); 等待异步任务完成。

            6.当异步任务完成后,主线程执行 executor.shutdown(); 关闭线程池。

            7.最后,主线程输出“主线程结束”。

runAsync(异步执行任务,任务没有返回值)

         

         

        执行无返回值的异步任务(基于 Runnable),返回 CompletableFuture<Void>,表示没有结果返回。

        runAsync 方法属于 CompletableFuture 类的一个静态方法。它用于在异步线程中执行不返回结果的任务。

        示例1:基本使用

         

        执行结果:

        

        示例2:自定义线程池的使用

         

           

         执行结果:

          

总结 

  • 当你需要在异步任务中计算和返回一个值时,使用 supplyAsync。
  • 当你只需要执行一些操作而不需要返回结果时,使用 runAsync。

      主要区别:

       

等待结果

        可以使用 join() 或 get() 方法等待计算结果,join() 方法不会抛出检查异常,而 get() 方法会抛出。

join

        等待 CompletableFuture 完成,返回结果,若有异常则抛出。

        

        join() 方法是 CompletableFuture 类中的一个重要方法,用于阻塞当前线程,直到异步任务完成并返回结果。它会以 CompletionException 的形式处理异常。

       示例1:基本使用

           

        代码说明:

        1.示例中,supplyAsync 方法启动了一个异步任务,该任务在后台线程中执行,模拟了一个 2 秒的延迟。

        2.使用 future.join() 方法,主线程会阻塞,直到异步任务完成并返回结果。

        3.当异步任务完成后,结果(42)将被打印。

        4.主线程结束。

        执行结果:

        

      示例 2:异常处理

      如果异步任务抛出异常,使用join()会抛出 CompletionException,

      不会抛出 InterruptedException,可以通过 getCause() 方法获取原始异常。

      使用 join() 可以方便地处理异步结果,并在需要时进行异常处理。

       

        代码说明:

        1.在这个示例中,异步任务抛出一个 RuntimeException。

        2.使用 join() 方法时,如果任务失败,CompletionException 将被抛出。

        3.可以通过 e.getCause() 获取原始异常并打印错误信息。

        执行结果:

         

get

       等待 CompletableFuture 完成并返回结果,但会抛出检查异常。

       

       get() 方法会抛出 InterruptedException 和 ExecutionException。

       InterruptedException 表示当前线程在等待结果时被中断。

       ExecutionException 包含计算过程中抛出的异常,可以通过 getCause() 方法获取原始异常。

       示例1:

       

       执行结果:

       

总结

  • join() 方法和 get() 方法都是 CompletableFuture 中用于获取异步计算结果的方法。
  • 两者都可以阻塞当前线程,直到异步计算完成并返回结果
  • 使用 get() 时需要处理 InterruptedException 和 ExecutionException,使得异常处理更加复杂。适用于需要对中断进行处理的场景,或者你需要捕获 InterruptedException 的情况。
  • 使用 join() 时只需要处理 CompletionException,使得代码更简洁。适用于更简单的异步处理场景,特别是在不需要管理中断的情况下。
  • 如果不需要关注中断,推荐使用 join() 方法。join() 更加简洁。

链式调用

        支持方法链,可以通过 thenAccept、 thenApply等方法添加后续操作。   
        

thenAccept(消费结果,无返回值‌)

         

        返回一个CompletableFuture<Void>,表示没有返回值。 

        在 CompletableFuture 完成时执行一个操作,但不返回结果。

        

         thenAccept()处理返回结果,不返回新的 CompletableFuture。

         示例1:

             

          说明:   

          1.thenAccept 接收一个 Consumer,在异步任务完成后处理结果,但不返回值。 

          执行结果:

           

thenApply

          

          在 CompletableFuture 完成时执行一个函数,处理其结果,返回一个新的 CompletableFuture。

         对上一个任务的结果进行同步转换,返回新值‌

         

          thenApply()处理返回结果并返回新的 CompletableFuture。

           示例1:

           

           说明:

           1.thenApply 接收一个 Function,在完成时对结果进行转换操作,并返回新的结果。

           执行结果:

           

           示例 2:

           使用 CompletableFuture 进行任务链式调用,同时结合 thenApply 和 thenAccept 方法。

           链式调用 CompletableFuture 来处理异步计算的结果。

           thenApply 用于对结果进行转换,而 thenAccept 用于处理最终结果。

           使用 join() 可以在需要时阻塞主线程,直到异步任务完成。

           

           执行流程:           

           1.启动异步任务:

                      supplyAsync 启动一个异步任务,模拟一个 2 秒的长时间运行任务,返回结果 10。

            2.第一个 thenApply:

                     在第一个 thenApply 中,接收结果 10,再模拟一个 1 秒的长时间运行任务,将结果翻倍,返回 20。

            3.第二个 thenApply:

                      在第二个 thenApply 中,将上一个结果 20 增加 1,返回 21。

            4.输出最终结果:

                      使用 thenAccept 输出最终结果 21。这一步不会阻塞主线程。

            5.主线程继续执行:

                      主线程打印 "Doing other work...",并继续执行。

             6.使用 join():

                       主线程调用 future.join(),这会阻塞主线程,直到异步任务完成并返回结果 21。

             7.打印结果:

                       最后,主线程打印结果结束信息

总结

  • thenAccept:用于处理异步计算的结果,但不返回任何结果适用于只关心结果的场景
  • thenApply:用于对结果进行转换并返回一个新的结果适用于需要对结果进行进一步处理的场景

任务组合(组合多个 CompletableFuture)

         可以通过 thenCombine、thenCompose 等方法将多个CompletableFuture 结合在一起。

         thenCombine 和 thenCompose 是 CompletableFuture 类中用于组合多个异步计算结果的方法。它们在处理多个 CompletableFuture 时非常有用。

thenCombine(合并两个独立任务的结果‌)

          

         结合两个CompletableFuture 的结果,在它们都完成后执行操作。

         

         thenCombine 用于将两个独立的 CompletableFuture 结果组合在一起。

         当两个 CompletableFuture 都完成时,它会执行一个合并操作,组合它们的结果,并返回一个新的 CompletableFuture

         示例1:

                

          说明: 

          1.创建两个异步任务:

                    future1 和 future2 分别返回 10 和 20,各自模拟了不同的延迟时间。

          2.使用 thenCombine:

                    当 future1 和 future2 都完成后,thenCombine 会被调用,合并两个结果并返回 30。

          3.打印结果:

                    使用 thenAccept 打印合并后的结果。

          4.等待完成:

                    combinedFuture.join() 阻止主线程继续执行,直到合并的 CompletableFuture 完成。

          执行结果:

          

thenCompose

        

        将一个 CompletableFuture 的结果作为另一个 CompletableFuture 的输入,用于链式调用。返回另一个 CompletableFuture。 

       

       thenCompose 用于将一个 CompletableFuture 的结果转化为另一个 CompletableFuture。它常用于需要链式调用,且后一个操作依赖于前一个操作的结果的情况。

       示例1:

       

      说明:

      1.创建第一个异步任务:

                使用 supplyAsync 创建一个异步任务,返回 10,并模拟延迟。

      2.使用 thenCompose:

                 当第一个任务完成时,thenCompose 会被调用。

                 它返回另一个 CompletableFuture,该任务将前一个结果(10)乘以 2,并模拟延迟。

      3.打印最终结果:

                 使用 thenAccept 打印最终结果 20。

      4.等待完成:

               composedFuture.join() 确保主线程等待这个组合的 CompletableFuture 完成,防止程序过早退出。

        执行结果:

        

总结

  • thenCombine:

       用于合并两个独立的 CompletableFuture 的结果。当两个 CompletableFuture 都完成时,执行合并操作。

  • thenCompose:

       将一个 CompletableFuture 的结果映射到另一个 CompletableFuture,前一个任务的结果用于下一个任务。

异常处理

        在CompletableFuture中,可以用exceptionally 和 handle 方法处理异常情况。   

exceptionally

       为 CompletableFuture 定义异常处理逻辑。

        exceptionally 方法用于处理 CompletableFuture 中发生的异常。如果在计算过程中发生了异常,该方法会被调用,并允许你提供一个替代的结果。 

       

       示例1:

              

       执行结果:

       

handle

       在计算完成后处理结果和异常,可以返回新的结果。

       handle 方法也可以用于处理异常,它不仅可以处理异常,还可以处理正常结果。它接受两个参数,一个是正常结果,另一个是异常信息(如果有的话)。它的返回值可以是一个新的结果,无论是正常的还是替代的。

      

      示例1:

      

      执行结果:   

       

总结

  • exceptionally: 主要用于处理异常,返回一个替代值。
  • handle: 可以处理正常结果和异常,返回一个新的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值