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

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

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

相关推荐
老鼠只爱大米1 小时前
Java设计模式之装饰器模式详解
java·设计模式·装饰器模式·decorator·java设计模式
i***l9201 小时前
使用 Spring Boot 实现图片上传
spring boot·后端·状态模式
LSL666_1 小时前
7 SpringBoot pom.xml解释
java·spring boot·spring
ps酷教程1 小时前
java泛型反射&mybatis的TypeParameterResolver
java·mybatis
b***59431 小时前
springboot+mybaties项目中扫描不到@mapper注解的解决方法
java·spring boot·mybatis
i***17181 小时前
SpringBoot Maven 项目 pom 中的 plugin 插件用法整理
spring boot·后端·maven
u***42071 小时前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端
慕沐.1 小时前
【算法】冒泡排序的原理及实现
java·算法·排序算法
v***8571 小时前
Java进阶-在Ubuntu上部署SpringBoot应用
java·spring boot·ubuntu