当前位置:   article > 正文

精通多线程,没用过CompletableFuture ?_completablefuture thenaccept和thenacceptasync

completablefuture thenaccept和thenacceptasync

一安 一安未来 2023-08-22 08:00 发表于北京

收录于合集#干货分享集147个

图片

大家好,我是一安~

图片

一个示例回顾Future

一些业务场景我们需要使用多线程异步执行任务,加快任务执行速度。

JDK5新增了Future接口,用于描述一个异步计算的结果。

虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.get()的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone 任务是否结束,再获取结果。

这两种处理方式都不是很优雅,相关代码如下:

  1. @Test  
  2. public void testFuture() throws ExecutionException, InterruptedException {  
  3.     ExecutorService executorService = Executors.newFixedThreadPool(5);  
  4.     Future<String> future = executorService.submit(() -> {  
  5.         Thread.sleep(2000);  
  6.         return "hello";  
  7.     });  
  8.     System.out.println(future.get());  
  9.     System.out.println("end");  
  10. }  

与此同时,Future无法解决多个异步任务需要相互依赖的场景,简单点说就是,主线程需要等待子线程任务执行完毕之后再进行执行,这个时候你可能想到了CountDownLatch,没错确实可以解决,代码如下。

这里定义两个Future,第一个通过用户id获取用户信息,第二个通过商品id获取商品信息。

  1. @Test  
  2.  public void testCountDownLatch() throws InterruptedException, ExecutionException {  
  3.      ExecutorService executorService = Executors.newFixedThreadPool(5);  
  4.      CountDownLatch downLatch = new CountDownLatch(2);  
  5.      long startTime = System.currentTimeMillis();  
  6.      Future<String> userFuture = executorService.submit(() -> {  
  7.          //模拟查询商品耗时500毫秒  
  8.          Thread.sleep(500);  
  9.          downLatch.countDown();  
  10.          return "用户A";  
  11.      });  
  12.      Future<String> goodsFuture = executorService.submit(() -> {  
  13.          //模拟查询商品耗时500毫秒  
  14.          Thread.sleep(400);  
  15.          downLatch.countDown();  
  16.          return "商品A";  
  17.      });  
  18.      downLatch.await();  
  19.      //模拟主程序耗时时间  
  20.      Thread.sleep(600);  
  21.      System.out.println("获取用户信息:" + userFuture.get());  
  22.      System.out.println("获取商品信息:" + goodsFuture.get());  
  23.      System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");  
  24.  }  

「运行结果」

  1. 获取用户信息:用户A  
  2. 获取商品信息:商品A  
  3. 总共用时1110ms  

从运行结果可以看出结果都已经获取,而且如果我们不用异步操作,执行时间应该是:500+400+600 = 1500,用异步操作后实际只用1110ms。

但是Java8以后我不再认为这是一种优雅的解决方式,接下来来了解下CompletableFuture的使用。

通过CompletableFuture实现上面示例

  1. @Test  
  2. public void testCompletableInfo() throws InterruptedException, ExecutionException {  
  3.     long startTime = System.currentTimeMillis();  
  4.       //调用用户服务获取用户基本信息  
  5.       CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() ->  
  6.               //模拟查询商品耗时500毫秒  
  7.       {  
  8.           try {  
  9.               Thread.sleep(500);  
  10.           } catch (InterruptedException e) {  
  11.               e.printStackTrace();  
  12.           }  
  13.           return "用户A";  
  14.       });  
  15.       //调用商品服务获取商品基本信息  
  16.       CompletableFuture<String> goodsFuture = CompletableFuture.supplyAsync(() ->  
  17.               //模拟查询商品耗时500毫秒  
  18.       {  
  19.           try {  
  20.               Thread.sleep(400);  
  21.           } catch (InterruptedException e) {  
  22.               e.printStackTrace();  
  23.           }  
  24.           return "商品A";  
  25.       });  
  26.       System.out.println("获取用户信息:" + userFuture.get());  
  27.       System.out.println("获取商品信息:" + goodsFuture.get());  
  28.       //模拟主程序耗时时间  
  29.       Thread.sleep(600);  
  30.       System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");  
  31. }  

运行结果

  1. 获取用户信息:用户A  
  2. 获取商品信息:商品A  
  3. 总共用时1112ms  

通过CompletableFuture可以很轻松的实现CountDownLatch的功能,你以为这就结束了,远远不止,CompletableFuture比这要强多了。

比如可以实现:任务1执行完了再执行任务2,甚至任务1执行的结果,作为任务2的入参数等等强大功能,下面就来学学CompletableFuture的API。

CompletableFuture创建方式

1、常用的4种创建方式

CompletableFuture源码中有四个静态方法用来执行异步任务

  1. public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){..}  
  2. public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor){..}  
  3. public static CompletableFuture<VoidrunAsync(Runnable runnable){..}  
  4. public static CompletableFuture<VoidrunAsync(Runnable runnable,Executor executor){..}  

一般我们用上面的静态方法来创建CompletableFuture,这里也解释下他们的区别:

  • 「supplyAsync」执行任务,支持返回值。

  • 「runAsync」执行任务,没有返回值。

「supplyAsync方法」

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

「runAsync方法」

  1. //使用默认内置线程池ForkJoinPool.commonPool(),根据runnable构建执行任务  
  2. public static CompletableFuture<Void> runAsync(Runnable runnable)   
  3. //自定义线程,根据runnable构建执行任务  
  4. public static CompletableFuture<Void> runAsync(Runnable runnable,  Executor executor)  
2、结果获取的4种方式

对于结果的获取CompltableFuture类提供了四种方式

  1. //方式一  
  2. public T get()  
  3. //方式二  
  4. public T get(long timeout, TimeUnit unit)  
  5. //方式三  
  6. public T getNow(T valueIfAbsent)  
  7. //方式四  
  8. public T join()  

说明:

  • 「get()和get(long timeout, TimeUnit unit)」 => 在Future中就已经提供了,后者提供超时处理,如果在指定时间内未获取结果将抛出超时异常

  • 「getNow」 => 立即获取结果不阻塞,结果计算已完成将返回结果或计算过程中的异常,如果未计算完成将返回设定的valueIfAbsent值

  • 「join」 => 方法里不会抛出异常

示例:

  1. @Test  
  2. public void testCompletableGet() throws InterruptedException, ExecutionException {  
  3.     CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {  
  4.         try {  
  5.             Thread.sleep(1000);  
  6.         } catch (InterruptedException e) {  
  7.             e.printStackTrace();  
  8.         }  
  9.         return "商品A";  
  10.     });  
  11.     // getNow方法测试   
  12.     System.out.println(cp1.getNow("商品B"));  
  13.     //join方法测试   
  14.     CompletableFuture<Integer> cp2 = CompletableFuture.supplyAsync((() -> 1 / 0));  
  15.     System.out.println(cp2.join());  
  16.     System.out.println("-----------------------------------------------------");  
  17.     //get方法测试  
  18.     CompletableFuture<Integer> cp3 = CompletableFuture.supplyAsync((() -> 1 / 0));  
  19.     System.out.println(cp3.get());  
  20. }  

「运行结果」

  • 第一个执行结果为 「商品B」,因为要先睡上1秒结果不能立即获取

  • join方法获取结果方法里不会抛异常,但是执行结果会抛异常,抛出的异常为CompletionException

  • get方法获取结果方法里将抛出异常,执行结果抛出的异常为ExecutionException

异步回调方法

图片

1、thenRun/thenRunAsync

通俗点讲就是,「做完第一个任务后,再做第二个任务,第二个任务也没有返回值」

示例

  1. @Test  
  2. public void testCompletableThenRunAsync() throws InterruptedException, ExecutionException {  
  3.     long startTime = System.currentTimeMillis();  
  4.       
  5.     CompletableFuture<Void> cp1 = CompletableFuture.runAsync(() -> {  
  6.         try {  
  7.             //执行任务A  
  8.             Thread.sleep(600);  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.     });  
  13.     CompletableFuture<Void> cp2 =  cp1.thenRun(() -> {  
  14.         try {  
  15.             //执行任务B  
  16.             Thread.sleep(400);  
  17.         } catch (InterruptedException e) {  
  18.             e.printStackTrace();  
  19.         }  
  20.     });  
  21.     // get方法测试  
  22.     System.out.println(cp2.get());  
  23.     //模拟主程序耗时时间  
  24.     Thread.sleep(600);  
  25.     System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");  
  26. }  
  27.   
  28. //运行结果    
  29. /**  
  30.  *  null  
  31.  *  总共用时1610ms  
  32.  */  

「thenRun 和thenRunAsync有什么区别呢?」

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

  • 调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。

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

说明: 后面介绍的thenAcceptthenAcceptAsyncthenApplythenApplyAsync等,它们之间的区别也是这个。

2、thenAccept/thenAcceptAsync

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

示例

  1. @Test  
  2. public void testCompletableThenAccept() throws ExecutionException, InterruptedException {  
  3.     long startTime = System.currentTimeMillis();  
  4.     CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {  
  5.         return "dev";  
  6.     });  
  7.     CompletableFuture<Void> cp2 =  cp1.thenAccept((a) -> {  
  8.         System.out.println("上一个任务的返回结果为: " + a);  
  9.     });  
  10.    
  11.     cp2.get();  
  12. }  
3、 thenApply/thenApplyAsync

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

示例

  1. @Test  
  2. public void testCompletableThenApply() throws ExecutionException, InterruptedException {  
  3.     CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {  
  4.         return "dev";  
  5.     }).thenApply((a) -> {  
  6.         if(Objects.equals(a,"dev")){  
  7.             return "dev";  
  8.         }  
  9.         return "prod";  
  10.     });  
  11.     System.out.println("当前环境为:" + cp1.get());  
  12.     //输出: 当前环境为:dev  
  13. }  

异常回调

CompletableFuture的任务不论是正常完成还是出现异常它都会调用 「whenComplete」这回调函数。

  • 「正常完成」:whenComplete返回结果和上级任务一致,异常为null;

  • 「出现异常」:whenComplete返回结果为null,异常为上级任务的异常;

即调用get()时,正常完成时就获取到结果,出现异常时就会抛出异常,需要你处理该异常。

下面来看看示例

1、只用whenComplete
  1. @Test  
  2. public void testCompletableWhenComplete() throws ExecutionException, InterruptedException {  
  3.     CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {  
  4.         if (Math.random() < 0.5) {  
  5.             throw new RuntimeException("出错了");  
  6.         }  
  7.         System.out.println("正常结束");  
  8.         return 0.11;  
  9.     }).whenComplete((aDouble, throwable) -> {  
  10.         if (aDouble == null) {  
  11.             System.out.println("whenComplete aDouble is null");  
  12.         } else {  
  13.             System.out.println("whenComplete aDouble is " + aDouble);  
  14.         }  
  15.         if (throwable == null) {  
  16.             System.out.println("whenComplete throwable is null");  
  17.         } else {  
  18.             System.out.println("whenComplete throwable is " + throwable.getMessage());  
  19.         }  
  20.     });  
  21.     System.out.println("最终返回的结果 = " + future.get());  
  22. }  

正常完成,没有异常时:

  1. 正常结束  
  2. whenComplete aDouble is 0.11  
  3. whenComplete throwable is null  
  4. 最终返回的结果 = 0.11  

出现异常时:get()会抛出异常

  1. whenComplete aDouble is null  
  2. whenComplete throwable is java.lang.RuntimeException: 出错了  
  3.   
  4. java.util.concurrent.ExecutionException: java.lang.RuntimeException: 出错了  
  5.  at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)  
  6.  at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)  
2、whenComplete + exceptionally示例
  1. @Test  
  2. public void testWhenCompleteExceptionally() throws ExecutionException, InterruptedException {  
  3.     CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {  
  4.         if (Math.random() < 0.5) {  
  5.             throw new RuntimeException("出错了");  
  6.         }  
  7.         System.out.println("正常结束");  
  8.         return 0.11;  
  9.     }).whenComplete((aDouble, throwable) -> {  
  10.         if (aDouble == null) {  
  11.             System.out.println("whenComplete aDouble is null");  
  12.         } else {  
  13.             System.out.println("whenComplete aDouble is " + aDouble);  
  14.         }  
  15.         if (throwable == null) {  
  16.             System.out.println("whenComplete throwable is null");  
  17.         } else {  
  18.             System.out.println("whenComplete throwable is " + throwable.getMessage());  
  19.         }  
  20.     }).exceptionally((throwable) -> {  
  21.         System.out.println("exceptionally中异常:" + throwable.getMessage());  
  22.         return 0.0;  
  23.     });  
  24.     System.out.println("最终返回的结果 = " + future.get());  
  25. }  

当出现异常时,exceptionally中会捕获该异常,给出默认返回值0.0。

  1. whenComplete aDouble is null  
  2. whenComplete throwable is java.lang.RuntimeException: 出错了  
  3. exceptionally中异常:java.lang.RuntimeException: 出错了  
  4. 最终返回的结果 = 0.0  

多任务组合回调

图片

1、AND组合关系

thenCombine / thenAcceptBoth / runAfterBoth都表示:「当任务一和任务二都完成再执行任务三」

区别在于:

  • 「runAfterBoth」 不会把执行结果当做方法入参,且没有返回值

  • 「thenAcceptBoth」: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值

  • 「thenCombine」:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值

示例

  1. @Test  
  2. public void testCompletableThenCombine() throws ExecutionException, InterruptedException {  
  3.     //创建线程池  
  4.     ExecutorService executorService = Executors.newFixedThreadPool(10);  
  5.     //开启异步任务1  
  6.     CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {  
  7.         System.out.println("异步任务1,当前线程是:" + Thread.currentThread().getId());  
  8.         int result = 1 + 1;  
  9.         System.out.println("异步任务1结束");  
  10.         return result;  
  11.     }, executorService);  
  12.     //开启异步任务2  
  13.     CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {  
  14.         System.out.println("异步任务2,当前线程是:" + Thread.currentThread().getId());  
  15.         int result = 1 + 1;  
  16.         System.out.println("异步任务2结束");  
  17.         return result;  
  18.     }, executorService);  
  19.     //任务组合  
  20.     CompletableFuture<Integer> task3 = task.thenCombineAsync(task2, (f1, f2) -> {  
  21.         System.out.println("执行任务3,当前线程是:" + Thread.currentThread().getId());  
  22.         System.out.println("任务1返回值:" + f1);  
  23.         System.out.println("任务2返回值:" + f2);  
  24.         return f1 + f2;  
  25.     }, executorService);  
  26.     Integer res = task3.get();  
  27.     System.out.println("最终结果:" + res);  
  28. }  

「运行结果」

  1. 异步任务1,当前线程是:17  
  2. 异步任务1结束  
  3. 异步任务2,当前线程是:18  
  4. 异步任务2结束  
  5. 执行任务3,当前线程是:19  
  6. 任务1返回值:2  
  7. 任务2返回值:2  
  8. 最终结果:4  
2、OR组合关系

applyToEither / acceptEither / runAfterEither 都表示:「两个任务,只要有一个任务完成,就执行任务三」

区别在于:

  • 「runAfterEither」:不会把执行结果当做方法入参,且没有返回值

  • 「acceptEither」: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值

  • 「applyToEither」:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值

示例

  1. @Test  
  2. public void testCompletableEitherAsync() {  
  3.     //创建线程池  
  4.     ExecutorService executorService = Executors.newFixedThreadPool(10);  
  5.     //开启异步任务1  
  6.     CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {  
  7.         System.out.println("异步任务1,当前线程是:" + Thread.currentThread().getId());  
  8.         int result = 1 + 1;  
  9.         System.out.println("异步任务1结束");  
  10.         return result;  
  11.     }, executorService);  
  12.     //开启异步任务2  
  13.     CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {  
  14.         System.out.println("异步任务2,当前线程是:" + Thread.currentThread().getId());  
  15.         int result = 1 + 2;  
  16.         try {  
  17.             Thread.sleep(3000);  
  18.         } catch (InterruptedException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.         System.out.println("异步任务2结束");  
  22.         return result;  
  23.     }, executorService);  
  24.     //任务组合  
  25.     task.acceptEitherAsync(task2, (res) -> {  
  26.         System.out.println("执行任务3,当前线程是:" + Thread.currentThread().getId());  
  27.         System.out.println("上一个任务的结果为:"+res);  
  28.     }, executorService);  
  29. }  

运行结果

  1. //通过结果可以看出,异步任务2都没有执行结束,任务3获取的也是1的执行结果  
  2. 异步任务1,当前线程是:17  
  3. 异步任务1结束  
  4. 异步任务2,当前线程是:18  
  5. 执行任务3,当前线程是:19  
  6. 上一个任务的结果为:2  

注意

如果把上面的核心线程数改为1也就是

ExecutorService executorService = Executors.newFixedThreadPool(1);  

运行结果就是下面的了,会发现根本没有执行任务3,显然是任务3直接被丢弃了。

  1. 异步任务1,当前线程是:17  
  2. 异步任务1结束  
  3. 异步任务2,当前线程是:17  
3、多任务组合
  • 「allOf」:等待所有任务完成

  • 「anyOf」:只要有一个任务完成

示例

allOf:等待所有任务完成

  1. @Test  
  2. public void testCompletableAallOf() throws ExecutionException, InterruptedException {  
  3.     //创建线程池  
  4.     ExecutorService executorService = Executors.newFixedThreadPool(10);  
  5.     //开启异步任务1  
  6.     CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {  
  7.         System.out.println("异步任务1,当前线程是:" + Thread.currentThread().getId());  
  8.         int result = 1 + 1;  
  9.         System.out.println("异步任务1结束");  
  10.         return result;  
  11.     }, executorService);  
  12.     //开启异步任务2  
  13.     CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {  
  14.         System.out.println("异步任务2,当前线程是:" + Thread.currentThread().getId());  
  15.         int result = 1 + 2;  
  16.         try {  
  17.             Thread.sleep(3000);  
  18.         } catch (InterruptedException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.         System.out.println("异步任务2结束");  
  22.         return result;  
  23.     }, executorService);  
  24.     //开启异步任务3  
  25.     CompletableFuture<Integer> task3 = CompletableFuture.supplyAsync(() -> {  
  26.         System.out.println("异步任务3,当前线程是:" + Thread.currentThread().getId());  
  27.         int result = 1 + 3;  
  28.         try {  
  29.             Thread.sleep(4000);  
  30.         } catch (InterruptedException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.         System.out.println("异步任务3结束");  
  34.         return result;  
  35.     }, executorService);  
  36.     //任务组合  
  37.     CompletableFuture<Void> allOf = CompletableFuture.allOf(task, task2, task3);  
  38.     //等待所有任务完成  
  39.     allOf.get();  
  40.     //获取任务的返回结果  
  41.     System.out.println("task结果为:" + task.get());  
  42.     System.out.println("task2结果为:" + task2.get());  
  43.     System.out.println("task3结果为:" + task3.get());  
  44. }  

anyOf: 只要有一个任务完成

  1. @Test  
  2. public void testCompletableAnyOf() throws ExecutionException, InterruptedException {  
  3.     //创建线程池  
  4.     ExecutorService executorService = Executors.newFixedThreadPool(10);  
  5.     //开启异步任务1  
  6.     CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {  
  7.         int result = 1 + 1;  
  8.         return result;  
  9.     }, executorService);  
  10.     //开启异步任务2  
  11.     CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {  
  12.         int result = 1 + 2;  
  13.         return result;  
  14.     }, executorService);  
  15.     //开启异步任务3  
  16.     CompletableFuture<Integer> task3 = CompletableFuture.supplyAsync(() -> {  
  17.         int result = 1 + 3;  
  18.         return result;  
  19.     }, executorService);  
  20.     //任务组合  
  21.     CompletableFuture<Object> anyOf = CompletableFuture.anyOf(task, task2, task3);  
  22.     //只要有一个有任务完成  
  23.     Object o = anyOf.get();  
  24.     System.out.println("完成的任务的结果:" + o);  
  25. }  

CompletableFuture使用有哪些注意点

图片

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

1、Future需要获取返回值,才能获取异常信息
  1. @Test  
  2. public void testWhenCompleteExceptionally() {  
  3.     CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {  
  4.         if (1 == 1) {  
  5.             throw new RuntimeException("出错了");  
  6.         }  
  7.         return 0.11;  
  8.     });  
  9.     //如果不加 get()方法这一行,看不到异常信息  
  10.     //future.get();  
  11. }  

Future需要获取返回值,才能获取到异常信息。如果不加 get()/join()方法,看不到异常信息。

小伙伴们使用的时候,注意一下哈,考虑是否加try...catch...或者使用exceptionally方法。

2、CompletableFuture的get()方法是阻塞的

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

  1. //反例  
  2.  CompletableFuture.get();  
  3. //正例  
  4. CompletableFuture.get(5, TimeUnit.SECONDS);  
3、不建议使用默认线程池

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

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

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

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

一安未来

致力于Java,大数据;心得交流,技术分享;

103篇原创内容

公众号

如果这篇文章对你有所帮助,或者有所启发的话,帮忙 分享、收藏、点赞、在看,你的支持就是我坚持下去的最大动力!

图片

如何处理多线程事务回滚


TCP和UDP协议的区别以及原理


阿里巴巴开源Chat2DB v1.0.11 初体验

图片

收录于合集 #干货分享集

 147个

上一篇如何实现Spring Boot 优雅停机

阅读 124

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/926132
推荐阅读
相关标签
  

闽ICP备14008679号