java之使用CompletableFuture入门1
Java 17
-
简介
JDK中异步执行任务。
源码:
// A Future that may be explicitly completed (setting its value and status),
// and may be used as a CompletionStage, supporting dependent functions
// and actions that trigger upon its completion.
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
// ...
}
//
// 实现的2个接口
// A Future represents the result of an asynchronous computation.
public interface Future<V> {
// ...
}
// A stage of a possibly asynchronous computation,
// that performs an action or computes a value when another CompletionStage completes.
public interface CompletionStage<T> {
// ...
}
supplyAsync
注,这个自己用的比较多。
有返回值的任务。
supplyAsync 源码:
下文使用第一个。第二个可以自定义线程池。
代码:
private static void testCompletableFuture1() {
log.info("in testCompletableFuture");
StopWatch sw = new StopWatch();
sw.start();
CompletableFuture<LocalDateTime> task = CompletableFuture.supplyAsync(()->{
log.info("in CompletableFuture task");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return LocalDateTime.now();
});
sw.stop();
try {
log.info("等待结果...");
sw.start();
LocalDateTime ldt = task.get();
sw.stop();
log.info("结果={}", ldt);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} finally {
log.info("耗时:{}毫秒,sw=\n{}", sw.getTotalTimeMillis(), sw.prettyPrint());
}
}
结果:
17:25:37.697 [main] INFO com.example.test.TestMain -- in testCompletableFuture 17:25:37.706 [ForkJoinPool.commonPool-worker-1] INFO com.example.test.TestMain -- in CompletableFuture task 17:25:37.706 [main] INFO com.example.test.TestMain -- 等待结果... 17:25:41.714 [main] INFO com.example.test.TestMain -- 结果=2024-09-28T17:25:41.713449200 17:25:41.749 [main] INFO com.example.test.TestMain -- 耗时:4010毫秒,sw= StopWatch '': running time = 4010560200 ns --------------------------------------------- ns % Task name --------------------------------------------- 003454300 000% 4007105900 100% |
get:
ForkJoinPool.commonPool-worker-1,线程名,使用了一个 ForkJoinPool。
造异常
结果:task.get() 调用时排除异常,没有输出结果。
17:27:45.155 [main] INFO com.example.test.TestMain -- in testCompletableFuture
17:27:45.163 [ForkJoinPool.commonPool-worker-1] INFO com.example.test.TestMain -- in CompletableFuture task
17:27:45.163 [main] INFO com.example.test.TestMain -- 等待结果...
17:27:45.184 [main] INFO com.example.test.TestMain -- 耗时:3毫秒,sw=
StopWatch '': running time = 3313300 ns
---------------------------------------------
ns % Task name
---------------------------------------------
003313300 100%
Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 造异常
at com.example.test.TestMain.testCompletableFuture1(TestMain.java:81)
at com.example.test.TestMain.main(TestMain.java:45)
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 造异常
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
at com.example.test.TestMain.testCompletableFuture1(TestMain.java:75)
... 1 more
Caused by: java.lang.RuntimeException: 造异常
at com.example.test.TestMain.lambda$testCompletableFuture1$0(TestMain.java:60)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
|
使用 exceptionally 处理异常
新增代码:
结果:task.get() 正常执行,没有抛出异常。
17:34:02.625 [main] INFO com.example.test.TestMain -- in testCompletableFuture
17:34:02.639 [ForkJoinPool.commonPool-worker-1] INFO com.example.test.TestMain -- in CompletableFuture task
17:34:02.640 [main] ERROR com.example.test.TestMain -- task 发生异常:e=
java.util.concurrent.CompletionException: java.lang.RuntimeException: 造异常
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1770)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Caused by: java.lang.RuntimeException: 造异常
at com.example.test.TestMain.lambda$testCompletableFuture1$0(TestMain.java:60)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
... 6 common frames omitted
17:34:02.646 [main] INFO com.example.test.TestMain -- 等待结果...
17:34:02.647 [main] INFO com.example.test.TestMain -- 结果=1970-01-01T00:00
17:34:02.682 [main] INFO com.example.test.TestMain -- 耗时:14毫秒,sw=
StopWatch '': running time = 14376300 ns
---------------------------------------------
ns % Task name
---------------------------------------------
014364800 100%
000011500 000%
|
注,这里时造了一个RuntimeException 异常,可以进一步造一个 InterruptedException 试试(TODO)。
构造器创建
通过 构造函数创建 CompletableFuture 对象:
CompletableFuture<String> task = new CompletableFuture<>();
代码:
private static void testCompletableFuture0() {
CompletableFuture<String> task = new CompletableFuture<>();
log.info("task={}, {}, {}", task.isCancelled(), task.isDone(), task.isCompletedExceptionally());
Thread nt = new Thread(() ->{
log.info("Thread nt 1");
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("Thread nt 2");
task.complete("nt done.");
});
nt.start();
try {
log.info("task.get()={}", task.get());
log.info("task.isx3={}, {}, {}", task.isCancelled(), task.isDone(), task.isCompletedExceptionally());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
log.info("end");
}
结果:
19:36:26.363 [main] INFO com.example.test.TestMain -- task=false, false, false 19:36:26.369 [Thread-0] INFO com.example.test.TestMain -- Thread nt 1 19:36:30.381 [Thread-0] INFO com.example.test.TestMain -- Thread nt 2 19:36:30.382 [main] INFO com.example.test.TestMain -- task.get()=nt done. 19:36:30.382 [main] INFO com.example.test.TestMain -- task.isx3=false, true, false 19:36:30.382 [main] INFO com.example.test.TestMain -- end |
死锁
注释掉上面的 “// task.complete("nt done.");”,此时,程序无法停止。
结果:
task.get() 一直卡住了。
19:38:49.402 [main] INFO com.example.test.TestMain -- task=false, false, false 19:38:49.412 [Thread-0] INFO com.example.test.TestMain -- Thread nt 1 19:38:53.419 [Thread-0] INFO com.example.test.TestMain -- Thread nt 2 |
runAsync
没有返回值。
runAsync 源码:
代码:
// 调用:
// testCompletableFuture2(1); // 无异常
// testCompletableFuture2(2); // 造异常
private static void testCompletableFuture2(int ctl) {
log.info("ctl={}", ctl);
// 形参为 Void
CompletableFuture<Void> task = CompletableFuture.runAsync(()->{
log.info("task 1");
if (ctl % 2 == 0) {
throw new RuntimeException("异常ctl=" + ctl);
}
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
log.error("task ex");
throw new RuntimeException(e);
}
log.info("task 3");
});
// .exceptionally(ex->{ // 处理异常
// log.error("exceptionally 处理异常:ex=", ex);
// return null;
// });
try {
log.info("2");
var x = task.get();
log.info("3.x={}", x);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} finally {
log.info("4.task.isx3={}, {}, {}", task.isCancelled(), task.isDone(), task.isCompletedExceptionally());
}
log.info("end");
}
结果:
20:03:37.633 [main] INFO com.example.test.TestMain -- ctl=1
20:03:37.646 [main] INFO com.example.test.TestMain -- 2
20:03:37.646 [ForkJoinPool.commonPool-worker-1] INFO com.example.test.TestMain -- task 1
20:03:41.655 [ForkJoinPool.commonPool-worker-1] INFO com.example.test.TestMain -- task 3
20:03:41.656 [main] INFO com.example.test.TestMain -- 3.x=null
20:03:41.657 [main] INFO com.example.test.TestMain -- 4.task.isx3=false, true, false
20:03:41.658 [main] INFO com.example.test.TestMain -- end
20:03:41.658 [main] INFO com.example.test.TestMain -- ctl=2
20:03:41.658 [main] INFO com.example.test.TestMain -- 2
20:03:41.658 [ForkJoinPool.commonPool-worker-1] INFO com.example.test.TestMain -- task 1
20:03:41.675 [main] INFO com.example.test.TestMain -- 4.task.isx3=false, true, true
Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 异常ctl=2
at com.example.test.TestMain.testCompletableFuture2(TestMain.java:85)
at com.example.test.TestMain.main(TestMain.java:50)
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: 异常ctl=2
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
at com.example.test.TestMain.testCompletableFuture2(TestMain.java:80)
... 1 more
Caused by: java.lang.RuntimeException: 异常ctl=2
at com.example.test.TestMain.lambda$testCompletableFuture2$0(TestMain.java:62)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
|
使用 exceptionally 处理异常
和 前面一样,不过,返回值为 return null。
修改的代码:
结果:
20:08:59.546 [main] INFO com.benzl.tl.back.api.TestMain -- ctl=1
20:08:59.558 [ForkJoinPool.commonPool-worker-1] INFO com.benzl.tl.back.api.TestMain -- task 1
20:08:59.558 [main] INFO com.benzl.tl.back.api.TestMain -- 2
20:09:03.563 [ForkJoinPool.commonPool-worker-1] INFO com.benzl.tl.back.api.TestMain -- task 3
20:09:03.565 [main] INFO com.benzl.tl.back.api.TestMain -- 3.x=null
20:09:03.565 [main] INFO com.benzl.tl.back.api.TestMain -- 4.task.isx3=false, true, false
20:09:03.565 [main] INFO com.benzl.tl.back.api.TestMain -- end
20:09:03.565 [main] INFO com.benzl.tl.back.api.TestMain -- ctl=2
20:09:03.566 [main] INFO com.benzl.tl.back.api.TestMain -- 2
20:09:03.566 [ForkJoinPool.commonPool-worker-1] INFO com.benzl.tl.back.api.TestMain -- task 1
20:09:03.580 [ForkJoinPool.commonPool-worker-1] ERROR com.benzl.tl.back.api.TestMain -- exceptionally 处理异常:ex=
java.util.concurrent.CompletionException: java.lang.RuntimeException: 异常ctl=2
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1807)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
Caused by: java.lang.RuntimeException: 异常ctl=2
at com.benzl.tl.back.api.TestMain.lambda$testCompletableFuture2$0(TestMain.java:62)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)
... 6 common frames omitted
20:09:03.596 [main] INFO com.benzl.tl.back.api.TestMain -- 3.x=null
20:09:03.597 [main] INFO com.benzl.tl.back.api.TestMain -- 4.task.isx3=false, true, false
20:09:03.597 [main] INFO com.benzl.tl.back.api.TestMain -- end
Process finished with exit code 0
|
注意,“4.task.isx3=false, true, false”,只有 中间一个 true 了。
---end---
或许还要写更多内容,更高级点的,比如,线程池、多个CompletableFuture对象 配合使用等……
参考资料
1、通俗易懂讲 CompletableFuture
https://www.cnblogs.com/cyrus-s/p/15485182.html
posted @ 2021-10-30 15:49 三木同学
2、1.java多线程之FutureTask、Future、CompletableFuture
2024-04-09
https://developer.aliyun.com/article/1478152
作者:程序三两行
3、
ben发布于博客园
ben发布于博客园