springboot 异步操作

有以下两个实现功能的方法

复制代码
    public String getName(){
        // 模拟功能的耗时操作
        try {
            Thread.sleep(2000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "csdn:sorako";
    }
    
    public String getCover(){
        // 模拟功能的耗时操作
        try {
            Thread.sleep(2000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "cover";
    }

有一个需求,需要异步调用获得结果之后,在执行数据库Mapper插入数据库操作(用打印表示

一、异步调用一个方法

如果是如下写法:

复制代码
        FileInfo fileInfo = new FileInfo();

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> getName());

        future.thenAccept(name -> fileInfo.setFileName(name)).exceptionally(ex -> {
            log.error("异步执行失败");
            return null;
        });

        //执行Mapper。。。。。。。。。。
        System.out.println("fileInfo = " + fileInfo);

答案是mapper执行sql后插入数据库的数据为空

在异步调用时,会直接执行主线程的后续部分,没有等异步执行返回结果就直接执行mapper中的操作了。

所以,可以把mapper操作放到回调函数中执行

复制代码
        FileInfo fileInfo = new FileInfo();

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> getName());

        future.thenAccept(name -> {
            fileInfo.setFileName(name);

            //执行Mapper。。。。。。。。。。
            System.out.println("fileInfo = " + fileInfo);
        }).exceptionally(ex -> {
            log.error("异步执行失败");
            return null;
        });

但是执行,会发现控制条没有任何信息????????

因为主线程已经结束了,异步调用还没走到mapper操作打印那里,但是如果在主线程还没结束,是会打印出来信息的。我们让主线程暂停几秒模拟一些后续操作。

结果如下

但是事实上我们无法预测是否主线程后续操作的结束时间前,异步调用能执行

所以我们可以让主线程必须在异步调用返回结果后在执行

复制代码
        FileInfo fileInfo = new FileInfo();

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> getName());

        future.thenAccept(name -> {
            fileInfo.setFileName(name);
            //执行Mapper。。。。。。。。。。
            System.out.println("fileInfo = " + fileInfo);
        }).exceptionally(ex -> {
            log.error("异步执行失败");
            return null;
        });

        // 等待异步任务完成
        try {
            future.get(); // 或者 future.join();
        } catch (Exception e) {
            log.error("等待异步任务时发生错误: " + e.getMessage());
        }

也可以简化:

在这里.get()会等待异步调用完成,但是也会可能抛出异常,需要抛出捕获,我们可以用.join()代替

这两个的区别(AI生成):

===================================================================

`future.get()` 和 `future.join()` 都是用于处理并发编程中异步任务的结果,但它们属于不同的编程框架或库,并且具有不同的用途和行为。

`future.get()`

  • **库**: Java 的 `java.util.concurrent.Future` 接口。

  • **用途**: 用于获取异步计算的结果。

  • **行为**:

  • 调用 `future.get()` 会阻塞调用线程,直到异步任务完成并返回结果。

  • 如果任务尚未完成,调用线程会被挂起,直到任务完成。

  • 如果任务执行过程中抛出异常,`future.get()` 会将异常重新抛出。

  • **示例**:

Future future = executorService.submit(() -> {

// 模拟一个异步任务

Thread.sleep(2000);

return 123;

});

try {

Integer result = future.get(); // 这会阻塞直到任务完成

System.out.println("Result: " + result);

} catch (InterruptedException | ExecutionException e) {

e.printStackTrace();

}

```

`future.join()`

  • **库**: Java 的 `Thread` 类或 Kotlin 的协程(`join()` 在 Kotlin 协程中表现为 `await()`)。

  • **用途**: 用于等待一个线程或协程完成。

  • **行为**:

  • 在 Java 中,`thread.join()` 会使当前线程(调用 `join()` 的线程)等待,直到目标线程终止。

  • **示例**:

Thread thread = new Thread(() -> {

// 模拟一个任务

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("Thread finished");

});

thread.start();

try {

thread.join(); // 这会阻塞直到线程完成

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("Main thread continues");

```

总结

  • `future.get()` 是 Java 并发库中的方法,用于获取异步任务的结果,并阻塞调用线程直到任务完成。

  • `join()` 是 Java 线程的方法,用于等待一个线程完成。

它们虽然都涉及到等待某个任务完成,但应用场景和所属的库不同。

=====================================================================

但是有人就会问了,在这里等待异步调用完成再执行后续操作,那不如不使用异步操作???好像确实是这样

那假如是执行两个异步操作呢?

二、异步调用两个方法

异步调用执行两个方法

复制代码
        FileInfo fileInfo = new FileInfo();

       CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> getName());
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> getCover());
        // 使用allOf等待所有Future完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);
        // 注册回调,当所有Future都完成时执行
        allFutures.thenRun(() -> {
            try {
                String fileName = future1.get();
                String fileCover = future2.get();
                fileInfo.setFileName(fileName);
                fileInfo.setFileCover(fileCover);
                //执行Mapper。。。。。。。。。。
                System.out.println("fileInfo = " + fileInfo);
            } catch (Exception e) {
                log.error("异步执行失败");
            }
        }).exceptionally(ex -> {
            ex.printStackTrace();
            return null;
        }).join();

当然,还是用使用.join()或者.get()保证异步调用执行完,这里我们可以看出执行时间2s.24ms

那如果不使用异步调用呢?执行时间4s.35ms

可以看出在多个异步调用执行复杂操作时,效率是更高的。

当然,这里的前提都是,主程序的后续执行需要异步调用后返回执行结果,如果不需要异步调用的返回结果。那首选异步操作。

相关推荐
小Ti客栈6 小时前
Spring Boot 集成 Springdoc-OpenAPI 与 Knife4j实现接口文档与可视化调试
java·spring boot·后端
Ai拆代码的曹操6 小时前
Spring 事务 REQUIRES_NEW 嵌套调用:连接池翻倍的秘密
java·后端·spring
动恰客流统计7 小时前
ReID边缘计算视觉统计:餐饮店客流增长的数字化破局路径
java·大数据·运维·人工智能
Ivanqhz8 小时前
Rust &‘static str浅析
java·前端·javascript·rust
weixin_4196583110 小时前
Docker 搭建 Jenkins 服务
java·docker·jenkins
captain37611 小时前
多线程线程安全问题
java·java-ee
CoderYanger11 小时前
A.每日一题:1979. 找出数组的最大公约数
java·程序人生·算法·leetcode·面试·职场和发展·学习方法
paopaokaka_luck11 小时前
基于Springboot3+Vue3的高校选课系统(AI选课助手、协同过滤算法、分享到微博、扣扣、Echarts图形化分析)
网络·spring boot·网络协议·echarts
灵极海11 小时前
LangChain4j RAG 实战完整指南:从入门到踩坑
java·langchain
yaoxin52112311 小时前
476. Java 反射 - 调用方法
java·开发语言