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发布于博客园

posted @ 2024-09-28 20:20  快乐的总统95  阅读(2)  评论(0编辑  收藏  举报