java之使用CompletableFuture入门2
Java 17
-
序章
本文介绍用过的 allOf、anyOf 函数的用法。
allOf 函数原型
两点:
1、没有返回值。
2、参数 cfs 中 任何一个 都不能是 null。
anyOf 函数原型
两点:
1、有返回值,为 Object。
2、参数 cfs 中 任何一个 都不能是 null。
allOf
测试意图:
多个任务正常执行。ben发布于博客园
任务中任何一个发生异常。
测试代码:
// 调用:
// testAllOf(false);
// testAllOf(true);
//
/**
* 测试 allOf 函数
* @param makeEx 制造异常
*/
private static void testAllOf(boolean makeEx) {
printWithTime("allOf 0: makeEx=" + makeEx);
CompletableFuture<String>[] arr = new CompletableFuture[4];
IntStream.range(1,4).forEach(i->{
arr[i-1] = CompletableFuture.supplyAsync(()->{
var tn = Thread.currentThread().getName();
printWithTime("start: " + tn + ", i=" + i);
try {
// 2 4 6
TimeUnit.SECONDS.sleep(i * 2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
printWithTime("end: " + tn + ", i=" + i);
return "t#" + i;
});
});
int len = 3;
// 调试:发生异常
// 开关1:第4个是否发生异常
// arr[3] = CompletableFuture.supplyAsync(()->{
// var tn = Thread.currentThread().getName();
// printWithTime("start: " + tn + ", i=OUT");
//
// if (makeEx) {
// throw new RuntimeException("发生了异常...boom");
// }
//
// try {
// TimeUnit.SECONDS.sleep(5);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// printWithTime("end: " + tn + ", i=OUT");
// return "t#OUT";
// });
// len = 4;
printWithTime("allOf 1");
CompletableFuture<Void> allTasks = CompletableFuture.allOf(Arrays.copyOf(arr, len));
printWithTime("allOf 2");
try {
allTasks.get();
printWithTime("allOf 3");
} catch (InterruptedException e) {
printWithTime("e 1");
throw new RuntimeException(e);
} catch (ExecutionException e) {
printWithTime("e 2");
// 开关2:
throw new RuntimeException(e);
}
printWithTime("allOf 4");
for (CompletableFuture<String> cfs : arr) {
if (Objects.nonNull(cfs)) {
try {
printWithTime("result="+ cfs.get());
} catch (InterruptedException e) {
printWithTime("e 3");
throw new RuntimeException(e);
} catch (ExecutionException e) {
printWithTime("e 4");
throw new RuntimeException(e);
}
} else {
printWithTime("cfs is null");
}
}
printWithTime("allOf end.");
}
所有线程正常执行
调用 testAllOf(false) 的结果
三个线程在执行,都顺利执行了,分别等待了2、4、6秒。ben发布于博客园
所有执行完毕后,一起输出了结果。
[2024-10-06 20:45:41] allOf 0: makeEx=false [2024-10-06 20:45:41] allOf 1 [2024-10-06 20:45:41] allOf 2 [2024-10-06 20:45:41] start: ForkJoinPool.commonPool-worker-3, i=3 [2024-10-06 20:45:41] start: ForkJoinPool.commonPool-worker-2, i=2 [2024-10-06 20:45:41] start: ForkJoinPool.commonPool-worker-1, i=1 [2024-10-06 20:45:43] end: ForkJoinPool.commonPool-worker-1, i=1 [2024-10-06 20:45:45] end: ForkJoinPool.commonPool-worker-2, i=2 [2024-10-06 20:45:47] end: ForkJoinPool.commonPool-worker-3, i=3 [2024-10-06 20:45:47] allOf 3 [2024-10-06 20:45:47] allOf 4 [2024-10-06 20:45:47] result=t#1 [2024-10-06 20:45:47] result=t#2 [2024-10-06 20:45:47] result=t#3 [2024-10-06 20:45:47] cfs is null [2024-10-06 20:45:47] allOf end. |
线程执行发生异常
操作:打开 开关1,给数组添加第4给任务。ben发布于博客园
执行结果:
前3个正常执行了,第4个发生异常。
allOf 会等到 所有线程 都有结果——正常的 或 异常的。
allOf 中 任何一个发生异常时,allOf 发生异常。
[2024-10-06 20:53:29] allOf 0: makeEx=true [2024-10-06 20:53:29] allOf 1 [2024-10-06 20:53:29] allOf 2 [2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-4, i=OUT [2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-2, i=2 [2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-1, i=1 [2024-10-06 20:53:29] start: ForkJoinPool.commonPool-worker-3, i=3 [2024-10-06 20:53:31] end: ForkJoinPool.commonPool-worker-1, i=1 [2024-10-06 20:53:33] end: ForkJoinPool.commonPool-worker-2, i=2 [2024-10-06 20:53:35] end: ForkJoinPool.commonPool-worker-3, i=3 [2024-10-06 20:53:35] e 2 Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 发生了异常...boom |
上面在 e 2 发生异常,但是,throw 了异常,如果不抛出异常呢?
操作:关闭 开关2——注释掉 抛出异常。
执行结果:
e 2 后可以继续执行。
执行到 第4个发生异常的线程时,抛出异常。ben发布于博客园
[2024-10-06 20:58:01] allOf 0: makeEx=true [2024-10-06 20:58:01] allOf 1 [2024-10-06 20:58:01] allOf 2 [2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-4, i=OUT [2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-2, i=2 [2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-1, i=1 [2024-10-06 20:58:01] start: ForkJoinPool.commonPool-worker-3, i=3 [2024-10-06 20:58:03] end: ForkJoinPool.commonPool-worker-1, i=1 [2024-10-06 20:58:05] end: ForkJoinPool.commonPool-worker-2, i=2 [2024-10-06 20:58:07] end: ForkJoinPool.commonPool-worker-3, i=3 [2024-10-06 20:58:07] e 2 [2024-10-06 20:58:07] allOf 4 [2024-10-06 20:58:07] result=t#1 [2024-10-06 20:58:07] result=t#2 [2024-10-06 20:58:07] result=t#3 [2024-10-06 20:58:07] e 4 Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 发生了异常...boom |
处理异常的方式
当然,可以用 exceptionally(...) 函数来处理 异常,避免异常传到外面的处理流程中。
anyOf
任何一个 任务执行完,立即返回其结果——正常的、异常的。ben发布于博客园
注意,是 有返回值 的。
正常执行1
将 上面测试代码的 allOf 替换为 anyOf 进行测试。代码:
/**
* 测试 anyOf 函数
* @param makeEx 制造异常
*/
private static void testAnyOf(boolean makeEx) {
printWithTime("anyOf 0: makeEx=" + makeEx);
CompletableFuture<String>[] arr = new CompletableFuture[4];
IntStream.range(1,4).forEach(i->{
arr[i-1] = CompletableFuture.supplyAsync(()->{
var tn = Thread.currentThread().getName();
printWithTime("start: " + tn + ", i=" + i);
try {
// 2 4 6
TimeUnit.SECONDS.sleep(i * 2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
printWithTime("end: " + tn + ", i=" + i);
return "t#" + i;
});
});
int len = 3;
// 调试:发生异常
// 开关1:第4个是否发生异常
// arr[3] = CompletableFuture.supplyAsync(()->{
// var tn = Thread.currentThread().getName();
// printWithTime("start: " + tn + ", i=OUT");
//
// if (makeEx) {
// throw new RuntimeException("发生了异常...boom");
// }
//
// try {
// TimeUnit.SECONDS.sleep(5);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// printWithTime("end: " + tn + ", i=OUT");
// return "t#OUT";
// });
// len = 4;
printWithTime("anyOf 1");
CompletableFuture<Object> allTasks = CompletableFuture.anyOf(Arrays.copyOf(arr, len));
printWithTime("anyOf 2");
try {
Object x = allTasks.get();
printWithTime("anyOf 3: x=" + x);
} catch (InterruptedException e) {
printWithTime("e 1");
throw new RuntimeException(e);
} catch (ExecutionException e) {
printWithTime("e 2");
// 开关2:
throw new RuntimeException(e);
}
printWithTime("anyOf 4");
for (CompletableFuture<String> cfs : arr) {
if (Objects.nonNull(cfs)) {
try {
printWithTime("result="+ cfs.get());
} catch (InterruptedException e) {
printWithTime("e 3");
throw new RuntimeException(e);
} catch (ExecutionException e) {
printWithTime("e 4");
throw new RuntimeException(e);
}
} else {
printWithTime("cfs is null");
}
}
printWithTime("anyOf end.");
}
testAnyOf(false) 执行结果:ben发布于博客园
3个任务,休眠2 秒的先返回,anyOf get到结果了。
但是,其它2个任务 还是会继续执行完,在后面的代码 get时,出现等待的情况。
当然,因为 各个任务的 休眠时间不同,总是 休眠最短的 返回。
[2024-10-06 21:10:37] anyOf 0: makeEx=false Process finished with exit code 0 |
正常执行:随机结果
3个任务,全部休眠 4秒。ben发布于博客园
TimeUnit.SECONDS.sleep(4);
执行结果:
anyOf 3: x=t#1、2、3 都有可能,随机的。 |
线程执行发生异常
在上一步的基础上,打开开关1——取消注释。
休眠4秒后,抛出异常(和之前不同):
// 调试:发生异常
// 开关1:第4个是否发生异常
arr[3] = CompletableFuture.supplyAsync(()->{
var tn = Thread.currentThread().getName();
printWithTime("start: " + tn + ", i=OUT");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (makeEx) {
throw new RuntimeException("发生了异常...boom");
}
printWithTime("end: " + tn + ", i=OUT");
return "t#OUT";
});
len = 4;
有时候结果是正常的,可以走到 e 4;有时候结果是异常的,走到 e 2 就结束了。
符合预期。ben发布于博客园
同 allOf 函数,可以用 exceptionally 函数来处理——异常时内部处理,不干扰外面流程。
小结
一个场景:
在获取数据库数据时,可以同时获取多个,再在程序中进行计算。
---end---ben发布于博客园
本文链接:
https://www.cnblogs.com/luo630/p/18448304
参考资料
1、4.异步编程利器:CompletableFuture详解-Java开发实战
2021-06-06
作者:捡田螺的小男孩
https://juejin.cn/post/6970558076642394142
2、CompletableFuture 组合处理 allOf 和 anyOf太赞了!
2021-05-31
作者:码农架构
https://juejin.cn/post/6968286631614742535
3、
ben发布于博客园
ben发布于博客园