我的编程空间,编程开发者的网络收藏夹
学习永远不晚

异步编程利器:CompletableFuture详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

异步编程利器:CompletableFuture详解

一个例子回顾 Future

因为CompletableFuture实现了Future接口,我们先来回顾Future吧。

Future是Java5新加的一个接口,它提供了一种异步并行计算的功能。如果主线程需要执行一个很耗时的计算任务,我们就可以通过future把这个任务放到异步线程中执行。主线程继续处理其他任务,处理完成后,再通过Future获取计算结果。

来看个简单例子吧,假设我们有两个任务服务,一个查询用户基本信息,一个是查询用户勋章信息。如下,

  1. public class UserInfoService { 
  2.  
  3.     public UserInfo getUserInfo(Long userId) throws InterruptedException { 
  4.         Thread.sleep(300);//模拟调用耗时 
  5.         return new UserInfo("666""捡田螺的小男孩", 27); //一般是查数据库,或者远程调用返回的 
  6.     } 
  7.  
  8. public class MedalService { 
  9.  
  10.     public MedalInfo getMedalInfo(long userId) throws InterruptedException { 
  11.         Thread.sleep(500); //模拟调用耗时 
  12.         return new MedalInfo("666""守护勋章"); 
  13.     } 

接下来,我们来演示下,在主线程中是如何使用Future来进行异步调用的。

  1. public class FutureTest { 
  2.  
  3.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  4.  
  5.         ExecutorService executorService = Executors.newFixedThreadPool(10); 
  6.  
  7.         UserInfoService userInfoService = new UserInfoService(); 
  8.         MedalService medalService = new MedalService(); 
  9.         long userId =666L; 
  10.         long startTime = System.currentTimeMillis(); 
  11.  
  12.         //调用用户服务获取用户基本信息 
  13.         FutureTask userInfoFutureTask = new FutureTask<>(new Callable() { 
  14.             @Override 
  15.             public UserInfo call() throws Exception { 
  16.                 return userInfoService.getUserInfo(userId); 
  17.             } 
  18.         }); 
  19.         executorService.submit(userInfoFutureTask); 
  20.  
  21.         Thread.sleep(300); //模拟主线程其它操作耗时 
  22.  
  23.         FutureTask medalInfoFutureTask = new FutureTask<>(new Callable() { 
  24.             @Override 
  25.             public MedalInfo call() throws Exception { 
  26.                 return medalService.getMedalInfo(userId); 
  27.             } 
  28.         }); 
  29.         executorService.submit(medalInfoFutureTask); 
  30.  
  31.         UserInfo userInfo = userInfoFutureTask.get();//获取个人信息结果 
  32.         MedalInfo medalInfo = medalInfoFutureTask.get();//获取勋章信息结果 
  33.  
  34.         System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms"); 
  35.     } 
  36.      

运行结果:

  1. 总共用时806ms 

如果我们不使用Future进行并行异步调用,而是在主线程串行进行的话,耗时大约为300+500+300 = 1100 ms。可以发现,future+线程池异步配合,提高了程序的执行效率。

但是Future对于结果的获取,不是很友好,只能通过阻塞或者轮询的方式得到任务的结果。

  • Future.get() 就是阻塞调用,在线程获取结果之前get方法会一直阻塞。
  • Future提供了一个isDone方法,可以在程序中轮询这个方法查询执行结果。

阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的CPU资源。因此,JDK8设计出CompletableFuture。CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

一个例子走进CompletableFuture

我们还是基于以上Future的例子,改用CompletableFuture 来实现

  1. public class FutureTest { 
  2.  
  3.     public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { 
  4.  
  5.         UserInfoService userInfoService = new UserInfoService(); 
  6.         MedalService medalService = new MedalService(); 
  7.         long userId =666L; 
  8.         long startTime = System.currentTimeMillis(); 
  9.  
  10.         //调用用户服务获取用户基本信息 
  11.         CompletableFuture completableUserInfoFuture = CompletableFuture.supplyAsync(() -> userInfoService.getUserInfo(userId)); 
  12.  
  13.         Thread.sleep(300); //模拟主线程其它操作耗时 
  14.  
  15.         CompletableFuture completableMedalInfoFuture = CompletableFuture.supplyAsync(() -> medalService.getMedalInfo(userId));  
  16.  
  17.         UserInfo userInfo = completableUserInfoFuture.get(2,TimeUnit.SECONDS);//获取个人信息结果 
  18.         MedalInfo medalInfo = completableMedalInfoFuture.get();//获取勋章信息结果 
  19.         System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms"); 
  20.  
  21.     } 

可以发现,使用CompletableFuture,代码简洁了很多。CompletableFuture的supplyAsync方法,提供了异步执行的功能,线程池也不用单独创建了。实际上,它CompletableFuture使用了默认线程池是ForkJoinPool.commonPool。

CompletableFuture提供了几十种方法,辅助我们的异步任务场景。这些方法包括创建异步任务、任务异步回调、多个任务组合处理等方面。我们一起来学习吧

CompletableFuture使用场景

创建异步任务

CompletableFuture创建异步任务,一般有supplyAsync和runAsync两个方法

创建异步任务

  • supplyAsync执行CompletableFuture任务,支持返回值
  • runAsync执行CompletableFuture任务,没有返回值。

supplyAsync方法

//使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务

  1. //使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务 
  2. public static  CompletableFuture supplyAsync(Supplier supplier) 
  3. //自定义线程,根据supplier构建执行任务 
  4. public static  CompletableFuture supplyAsync(Supplier supplier, Executor executor) 

runAsync方法

  1. //使用默认内置线程池ForkJoinPool.commonPool(),根据runnable构建执行任务 
  2. public static CompletableFuture runAsync(Runnable runnable)  
  3. //自定义线程,根据runnable构建执行任务 
  4. public static CompletableFuture runAsync(Runnable runnable,  Executor executor) 

实例代码如下:

  1. public class FutureTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.         //可以自定义线程池 
  5.         ExecutorService executor = Executors.newCachedThreadPool(); 
  6.         //runAsync的使用 
  7.         CompletableFuture runFuture = CompletableFuture.runAsync(() -> System.out.println("run,关注公众号:捡田螺的小男孩"), executor); 
  8.         //supplyAsync的使用 
  9.         CompletableFuture supplyFuture = CompletableFuture.supplyAsync(() -> { 
  10.                     System.out.print("supply,关注公众号:捡田螺的小男孩"); 
  11.                     return "捡田螺的小男孩"; }, executor); 
  12.         //runAsync的future没有返回值,输出null 
  13.         System.out.println(runFuture.join()); 
  14.         //supplyAsync的future,有返回值 
  15.         System.out.println(supplyFuture.join()); 
  16.         executor.shutdown(); // 线程池需要关闭 
  17.     } 
  18. //输出 
  19. run,关注公众号:捡田螺的小男孩 
  20. null 
  21. supply,关注公众号:捡田螺的小男孩捡田螺的小男孩 

任务异步回调

1. thenRun/thenRunAsync

  1. public CompletableFuture thenRun(Runnable action); 
  2. public CompletableFuture thenRunAsync(Runnable action); 

CompletableFuture的thenRun方法,通俗点讲就是,做完第一个任务后,再做第二个任务。某个任务执行完成后,执行回调方法;但是前后两个任务没有参数传递,第二个任务也没有返回值

  1. public class FutureThenRunTest { 
  2.  
  3.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  4.  
  5.         CompletableFuture orgFuture = CompletableFuture.supplyAsync( 
  6.                 ()->{ 
  7.                     System.out.println("先执行第一个CompletableFuture方法任务"); 
  8.                     return "捡田螺的小男孩"
  9.                 } 
  10.         ); 
  11.  
  12.         CompletableFuture thenRunFuture = orgFuture.thenRun(() -> { 
  13.             System.out.println("接着执行第二个任务"); 
  14.         }); 
  15.  
  16.         System.out.println(thenRunFuture.get()); 
  17.     } 
  18. //输出 
  19. 先执行第一个CompletableFuture方法任务 
  20. 接着执行第二个任务 
  21. null 

thenRun 和thenRunAsync有什么区别呢?可以看下源码哈:

  1. private static final Executor asyncPool = useCommonPool ? 
  2.       ForkJoinPool.commonPool() : new ThreadPerTaskExecutor(); 
  3.        
  4.   public CompletableFuture thenRun(Runnable action) { 
  5.       return uniRunStage(nullaction); 
  6.   } 
  7.  
  8.   public CompletableFuture thenRunAsync(Runnable action) { 
  9.       return uniRunStage(asyncPool, action); 
  10.   } 

如果你执行第一个任务的时候,传入了一个自定义线程池:

  • 调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。
  • 调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池

TIPS: 后面介绍的thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也是这个哈。

2.thenAccept/thenAcceptAsync

CompletableFuture的thenAccept方法表示,第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,但是回调方法是没有返回值的。

  1. public class FutureThenAcceptTest { 
  2.  
  3.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  4.  
  5.         CompletableFuture orgFuture = CompletableFuture.supplyAsync( 
  6.                 ()->{ 
  7.                     System.out.println("原始CompletableFuture方法任务"); 
  8.                     return "捡田螺的小男孩"
  9.                 } 
  10.         ); 
  11.  
  12.         CompletableFuture thenAcceptFuture = orgFuture.thenAccept((a) -> { 
  13.             if ("捡田螺的小男孩".equals(a)) { 
  14.                 System.out.println("关注了"); 
  15.             } 
  16.  
  17.             System.out.println("先考虑考虑"); 
  18.         }); 
  19.  
  20.         System.out.println(thenAcceptFuture.get()); 
  21.     } 

3. thenApply/thenApplyAsync

CompletableFuture的thenApply方法表示,第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的。

  1. public class FutureThenApplyTest { 
  2.  
  3.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  4.  
  5.         CompletableFuture orgFuture = CompletableFuture.supplyAsync( 
  6.                 ()->{ 
  7.                     System.out.println("原始CompletableFuture方法任务"); 
  8.                     return "捡田螺的小男孩"
  9.                 } 
  10.         ); 
  11.  
  12.         CompletableFuture thenApplyFuture = orgFuture.thenApply((a) -> { 
  13.             if ("捡田螺的小男孩".equals(a)) { 
  14.                 return "关注了"
  15.             } 
  16.  
  17.             return "先考虑考虑"
  18.         }); 
  19.  
  20.         System.out.println(thenApplyFuture.get()); 
  21.     } 
  22. //输出 
  23. 原始CompletableFuture方法任务 
  24. 关注了 

4. exceptionally

CompletableFuture的exceptionally方法表示,某个任务执行异常时,执行的回调方法;并且有抛出异常作为参数,传递到回调方法。

  1. public class FutureExceptionTest { 
  2.  
  3.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  4.  
  5.         CompletableFuture orgFuture = CompletableFuture.supplyAsync( 
  6.                 ()->{ 
  7.                     System.out.println("当前线程名称:" + Thread.currentThread().getName()); 
  8.                     throw new RuntimeException(); 
  9.                 } 
  10.         ); 
  11.  
  12.         CompletableFuture exceptionFuture = orgFuture.exceptionally((e) -> { 
  13.             e.printStackTrace(); 
  14.             return "你的程序异常啦"
  15.         }); 
  16.  
  17.         System.out.println(exceptionFuture.get()); 
  18.     } 
  19. //输出 
  20. 当前线程名称:ForkJoinPool.commonPool-worker-1 
  21. java.util.concurrent.CompletionException: java.lang.RuntimeException 
  22.  at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) 
  23.  at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) 
  24.  at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592) 
  25.  at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582) 
  26.  at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) 
  27.  at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) 
  28.  at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) 
  29.  at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) 
  30. Caused by: java.lang.RuntimeException 
  31.  at cn.eovie.future.FutureWhenTest.lambda$main$0(FutureWhenTest.java:13) 
  32.  at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590) 
  33.  ... 5 more 
  34. 你的程序异常啦 

5. whenComplete方法

CompletableFuture的whenComplete方法表示,某个任务执行完成后,执行的回调方法,无返回值;并且whenComplete方法返回的CompletableFuture的result是上个任务的结果。

  1. public class FutureWhenTest { 
  2.  
  3.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  4.  
  5.         CompletableFuture orgFuture = CompletableFuture.supplyAsync( 
  6.                 ()->{ 
  7.                     System.out.println("当前线程名称:" + Thread.currentThread().getName()); 
  8.                     try { 
  9.                         Thread.sleep(2000L); 
  10.                     } catch (InterruptedException e) { 
  11.                         e.printStackTrace(); 
  12.                     } 
  13.                     return "捡田螺的小男孩"
  14.                 } 
  15.         ); 
  16.  
  17.         CompletableFuture rstFuture = orgFuture.whenComplete((a, throwable) -> { 
  18.             System.out.println("当前线程名称:" + Thread.currentThread().getName()); 
  19.             System.out.println("上个任务执行完啦,还把" + a + "传过来"); 
  20.             if ("捡田螺的小男孩".equals(a)) { 
  21.                 System.out.println("666"); 
  22.             } 
  23.             System.out.println("233333"); 
  24.         }); 
  25.  
  26.         System.out.println(rstFuture.get()); 
  27.     } 
  28. //输出 
  29. 当前线程名称:ForkJoinPool.commonPool-worker-1 
  30. 当前线程名称:ForkJoinPool.commonPool-worker-1 
  31. 上个任务执行完啦,还把捡田螺的小男孩传过来 
  32. 666 
  33. 233333 
  34. 捡田螺的小男孩 

6. handle方法

CompletableFuture的handle方法表示,某个任务执行完成后,执行回调方法,并且是有返回值的;并且handle方法返回的CompletableFuture的result是回调方法执行的结果。

  1. public class FutureHandlerTest { 
  2.  
  3.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  4.  
  5.         CompletableFuture orgFuture = CompletableFuture.supplyAsync( 
  6.                 ()->{ 
  7.                     System.out.println("当前线程名称:" + Thread.currentThread().getName()); 
  8.                     try { 
  9.                         Thread.sleep(2000L); 
  10.                     } catch (InterruptedException e) { 
  11.                         e.printStackTrace(); 
  12.                     } 
  13.                     return "捡田螺的小男孩"
  14.                 } 
  15.         ); 
  16.  
  17.         CompletableFuture rstFuture = orgFuture.handle((a, throwable) -> { 
  18.  
  19.             System.out.println("上个任务执行完啦,还把" + a + "传过来"); 
  20.             if ("捡田螺的小男孩".equals(a)) { 
  21.                 System.out.println("666"); 
  22.                 return "关注了"
  23.             } 
  24.             System.out.println("233333"); 
  25.             return null
  26.         }); 
  27.  
  28.         System.out.println(rstFuture.get()); 
  29.     } 
  30. //输出 
  31. 当前线程名称:ForkJoinPool.commonPool-worker-1 
  32. 上个任务执行完啦,还把捡田螺的小男孩传过来 
  33. 666 
  34. 关注了 

多个任务组合处理

AND组合关系

thenCombine / thenAcceptBoth / runAfterBoth都表示:将两个CompletableFuture组合起来,只有这两个都正常执行完了,才会执行某个任务。

区别在于:

  • thenCombine:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
  • thenAcceptBoth: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
  • runAfterBoth 不会把执行结果当做方法入参,且没有返回值。
  1. public class ThenCombineTest { 
  2.  
  3.     public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { 
  4.  
  5.         CompletableFuture first = CompletableFuture.completedFuture("第一个异步任务"); 
  6.         ExecutorService executor = Executors.newFixedThreadPool(10); 
  7.         CompletableFuture future = CompletableFuture 
  8.                 //第二个异步任务 
  9.                 .supplyAsync(() -> "第二个异步任务", executor) 
  10.                 // (w, s) -> System.out.println(s) 是第三个任务 
  11.                 .thenCombineAsync(first, (s, w) -> { 
  12.                     System.out.println(w); 
  13.                     System.out.println(s); 
  14.                     return "两个异步任务的组合"
  15.                 }, executor); 
  16.         System.out.println(future.join()); 
  17.         executor.shutdown(); 
  18.  
  19.     } 
  20. //输出 
  21. 第一个异步任务 
  22. 第二个异步任务 
  23. 两个异步任务的组合 

OR 组合的关系

applyToEither / acceptEither / runAfterEither 都表示:将两个CompletableFuture组合起来,只要其中一个执行完了,就会执行某个任务。

区别在于:

  • applyToEither:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值
  • acceptEither: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值
  • runAfterEither:不会把执行结果当做方法入参,且没有返回值。
  1. public class AcceptEitherTest { 
  2.     public static void main(String[] args) { 
  3.         //第一个异步任务,休眠2秒,保证它执行晚点 
  4.         CompletableFuture first = CompletableFuture.supplyAsync(()->{ 
  5.             try{ 
  6.  
  7.                 Thread.sleep(2000L); 
  8.                 System.out.println("执行完第一个异步任务");} 
  9.                 catch (Exception e){ 
  10.                     return "第一个任务异常"
  11.                 } 
  12.             return "第一个异步任务"
  13.         }); 
  14.         ExecutorService executor = Executors.newSingleThreadExecutor(); 
  15.         CompletableFuture future = CompletableFuture 
  16.                 //第二个异步任务 
  17.                 .supplyAsync(() -> { 
  18.                             System.out.println("执行完第二个任务"); 
  19.                             return "第二个任务";} 
  20.                 , executor) 
  21.                 //第三个任务 
  22.                 .acceptEitherAsync(first, System.out::println, executor); 
  23.  
  24.         executor.shutdown(); 
  25.     } 
  26. //输出 
  27. 执行完第二个任务 
  28. 第二个任务 

AllOf

所有任务都执行完成后,才执行 allOf返回的CompletableFuture。如果任意一个任务异常,allOf的CompletableFuture,执行get方法,会抛出异常

  1. public class allOfFutureTest { 
  2.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  3.  
  4.         CompletableFuture a = CompletableFuture.runAsync(()->{ 
  5.             System.out.println("我执行完了"); 
  6.         }); 
  7.         CompletableFuture b = CompletableFuture.runAsync(() -> { 
  8.             System.out.println("我也执行完了"); 
  9.         }); 
  10.         CompletableFuture allOfFuture = CompletableFuture.allOf(a, b).whenComplete((m,k)->{ 
  11.             System.out.println("finish"); 
  12.         }); 
  13.     } 
  14. //输出 
  15. 我执行完了 
  16. 我也执行完了 
  17. finish 

AnyOf

任意一个任务执行完,就执行anyOf返回的CompletableFuture。如果执行的任务异常,anyOf的CompletableFuture,执行get方法,会抛出异常

  1. public class AnyOfFutureTest { 
  2.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
  3.  
  4.         CompletableFuture a = CompletableFuture.runAsync(()->{ 
  5.             try { 
  6.                 Thread.sleep(3000L); 
  7.             } catch (InterruptedException e) { 
  8.                 e.printStackTrace(); 
  9.             } 
  10.             System.out.println("我执行完了"); 
  11.         }); 
  12.         CompletableFuture b = CompletableFuture.runAsync(() -> { 
  13.             System.out.println("我也执行完了"); 
  14.         }); 
  15.         CompletableFuture anyOfFuture = CompletableFuture.anyOf(a, b).whenComplete((m,k)->{ 
  16.             System.out.println("finish"); 
  17. //            return "捡田螺的小男孩"
  18.         }); 
  19.         anyOfFuture.join(); 
  20.     } 
  21. //输出 
  22. 我也执行完了 
  23. finish 
  24. thenCompose

    thenCompose方法会在某个任务执行完成后,将该任务的执行结果,作为方法入参,去执行指定的方法。该方法会返回一个新的CompletableFuture实例

    • 如果该CompletableFuture实例的result不为null,则返回一个基于该result新的CompletableFuture实例;
    • 如果该CompletableFuture实例为null,然后就执行这个新任务
    1. public class ThenComposeTest { 
    2.     public static void main(String[] args) throws ExecutionException, InterruptedException { 
    3.  
    4.         CompletableFuture f = CompletableFuture.completedFuture("第一个任务"); 
    5.         //第二个异步任务 
    6.         ExecutorService executor = Executors.newSingleThreadExecutor(); 
    7.         CompletableFuture future = CompletableFuture 
    8.                 .supplyAsync(() -> "第二个任务", executor) 
    9.                 .thenComposeAsync(data -> { 
    10.                     System.out.println(data); return f; //使用第一个任务作为返回 
    11.                 }, executor); 
    12.         System.out.println(future.join()); 
    13.         executor.shutdown(); 
    14.  
    15.     } 
    16. //输出 
    17. 第二个任务 
    18. 第一个任务 

    CompletableFuture使用有哪些注意点

    CompletableFuture 使我们的异步编程更加便利的、代码更加优雅的同时,我们也要关注下它,使用的一些注意点。

    1. Future需要获取返回值,才能获取异常信息

    1. ExecutorService executorService = new ThreadPoolExecutor(5, 10, 5L, 
    2.     TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); 
    3. CompletableFuture future = CompletableFuture.supplyAsync(() -> { 
    4.       int a = 0; 
    5.       int b = 666; 
    6.       int c = b / a; 
    7.       return true
    8.    },executorService).thenAccept(System.out::println); 
    9.     
    10.  //如果不加 get()方法这一行,看不到异常信息 
    11.  //future.get(); 

    Future需要获取返回值,才能获取到异常信息。如果不加 get()/join()方法,看不到异常信息。小伙伴们使用的时候,注意一下哈,考虑是否加try...catch...或者使用exceptionally方法。

    2. CompletableFuture的get()方法是阻塞的。

    CompletableFuture的get()方法是阻塞的,如果使用它来获取异步调用的返回值,需要添加超时时间~

    1. //反例 
    2.  CompletableFuture.get(); 
    3. //正例 
    4. CompletableFuture.get(5, TimeUnit.SECONDS); 

    3. 默认线程池的注意点

    CompletableFuture代码中又使用了默认的线程池,处理的线程个数是电脑CPU核数-1。在大量请求过来的时候,处理逻辑复杂的话,响应会很慢。一般建议使用自定义线程池,优化线程池配置参数。

    4. 自定义线程池时,注意饱和策略

    CompletableFuture的get()方法是阻塞的,我们一般建议使用future.get(3, TimeUnit.SECONDS)。并且一般建议使用自定义线程池。

    但是如果线程池拒绝策略是DiscardPolicy或者DiscardOldestPolicy,当线程池饱和时,会直接丢弃任务,不会抛弃异常。因此建议,CompletableFuture线程池策略最好使用AbortPolicy,然后耗时的异步线程,做好线程池隔离哈。

    参考资料

    [1]Java8 CompletableFuture 用法全解: https://blog.csdn.net/qq_31865983/article/details/106137777

    [2]基础篇:异步编程不会?我教你啊!: https://juejin.cn/post/6902655550031413262#heading-5

    [3]CompletableFuture get方法一直阻塞或抛出TimeoutException: https://blog.csdn.net/xiaolyuh123/article/details/85023269

    [4]编程老司机带你玩转 CompletableFuture 异步编程: https://zhuanlan.zhihu.com/p/111841508

    [5]解决CompletableFuture异常阻塞: https://blog.csdn.net/weixin_42742643/article/details/111638260

    本文转载自微信公众号「捡田螺的小男孩」,可以通过以下二维码关注。转载本文请联系捡田螺的小男孩公众号。

     

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    异步编程利器:CompletableFuture详解

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    异步编程利器:CompletableFuture详解

    最近刚好使用CompeletableFuture优化了项目中的代码,所以跟大家一起学习CompletableFuture。

    Java 8 异步编程 CompletableFuture 全解析

    Future 是 Java 5 添加的类,用来描述一个异步计算的结果。你可以使用 isDone() 方法检查计算是否完成,或者使用 get() 方法阻塞住调用线程,直到计算完成返回结果,也可以使用 cancel() 方法停止任务的执行。

    如何理解Java 8异步编程CompletableFuture

    本篇内容介绍了“如何理解Java 8异步编程CompletableFuture”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!本文大纲速看一、
    2023-06-15

    学会CompletableFuture轻松驾驭异步编程

    这篇文章主要为大家介绍了CompletableFuture轻松驾驭异步编程教程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-15

    并发编程:CompletableFuture异步编程没有那么难

    今天只是对于并发编程中的工具类使用和相关原理做了分享,在实际开发过程中可能需要考虑到更多的通用性,封装通过调用模版方法,不要每一个地方都写一堆类似的代码。

    CompletableFuture异步编程中的异常处理陷阱与解决方案

    在CompletableFuture异步编程中,异常处理是一个需要重点关注的问题。通过合理使用whenComplete、exceptionally和handle方法,并保留堆栈追踪信息,我们可以有效地处理异步任务中的异常,提高程序的稳定性和

    Java8中新增新特性异步编程之CompletableFuture

    Future是从JDK1.5开始有的,目的是获取异步任务执行的结果,通常情况会结合ExecutorService及Callable一起使用。
    Future异步JDK2024-11-30

    并发编程 | 从Future到CompletableFuture - 简化 Java 中的异步编程

    引言 在并发编程中,我们经常需要处理多线程的任务,这些任务往往具有依赖性,异步性,且需要在所有任务完成后获取结果。Java 8 引入了 CompletableFuture 类,它带来了一种新的编程模式,让我们能够以函数式编程的方式处理并发任
    2023-08-19

    强大的异步任务处理类CompletableFuture使用详解

    Future是从JDK1.5开始有的,目的是获取异步任务执行的结果,通常情况会结合ExecutorService及Callable一起使用。

    CompletableFuture:Java 8 中的异步编程利器

    CompletableFuture​ 作为 Java 8 引入的重要异步编程工具,极大地提升了 Java 平台在应对高并发、高性能场景的能力。

    rust异步编程详细讲解

    这篇文章主要介绍了rust异步编程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-16

    C#异步编程之async/await详解

    异步这个概念在不同语境下有不同的解释,不同的编程语言有不同异步编程方法,在C#语言中,常常使用async/await等关键字,和Task等类来实现异步编程。本文就来和大家聊聊async与await吧
    2023-03-11

    详谈nodejs异步编程

    目前需求中涉及到大量的异步操作,实际的页面越来越倾向于单页面应用。以后可以会使用backbone、angular、knockout等框架,但是关于异步编程的问题是首先需要面对的问题。随着node的兴起,异步编程成为一个非常热的话题。经过一段
    2022-06-04

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录