Springboot利用CompletableFuture异步执行线程(有回调和无回调)

目录

背景

实现

一、异步线程配置类

 二、自定义异步异常统一处理类

三、实现调用异步(无回调-runAsync())

四、实现调用异步(有回调-supplyAsync())  

五、异步执行错误异常示例

背景

项目中总会有需要异步执行来避免浪费时间资源的情况,这就需要异步操作。异步又分两种:

1、无回调:有一些执行过程对用户而言不需要反馈回调,只需要自己执行即可,且执行过程时间较长(某些第三方接口,如发送短信验证码、查取ip属地等等),如果同步执行,势必会影响到用户体验,这时候就可以使用CompletableFuture.runAsync()方法了。

2、有回调:在执行异步操作结束后,需要获得异步方法返回的值,然后再回调给用户展示,这时候就需要用到CompletableFuture.supplyAsync()方法了。

实现

一、异步线程配置类

/**
 * 异步线程配置类
 */
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(8);
        // 设置最大线程数
        executor.setMaxPoolSize(20);
        // 设置队列大小
        executor.setQueueCapacity(Integer.MAX_VALUE);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置线程名前缀+分组名称
        executor.setThreadNamePrefix("AsyncOperationThread-");
        executor.setThreadGroupName("AsyncOperationGroup");
        // 所有任务结束后关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化
        executor.initialize();
        return executor;
    }

    /**
     * 自定义异步异常
     * @return
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncException();
    }
}

 二、自定义异步异常统一处理类

/**
 * 异步请求异常错误
 */
public class AsyncException  implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
        System.out.println("--------------------异步请求异常捕获---------------------------------");
        System.out.println("Exception message - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
        System.out.println("---------------------异步请求异常捕获---------------------------------");
    }
}

三、实现调用异步(无回调-runAsync()

在需要异步的方法前加上@Async再调用即可如下,在service层中新建一个方法,模拟第三方接口请求延时3秒(异步方法和调用方法一定要写在不同的类中 ,如果写在一个类中,是没有效果的!)

@Service
public class LoginLogServiceImpl extends ServiceImpl<LoginLogMapper, LoginLog> implements ILoginLogService {

    // 无回调
    @Async
    public CompletableFuture<Void> test() {
        return CompletableFuture.runAsync(() -> {

            // 模拟第三方请求加载时间
            Thread.sleep(3000);

            System.out.println("保存成功登录日志");
        });
    }
}

 然后在controller层中调用该方法,

    @PostMapping("/login")
    public Result login() throws InterruptedException {

        System.out.println("登录验证成功");

        // 异步操作
        iLoginLogService.test();

        System.out.println("登录接口请求返回用户成功");

        // 正常返回结果
        return Result.success(200, "登录成功");
    }

执行完成后,我们打开控制台,可以看到, 异步请求在接口执行正常,在接口返回结果后执行完成。

四、实现调用异步(有回调-supplyAsync())  

这里我们多采用3个异步线程来模拟实际效果,且延迟时间为1 6 3,看下他们的异步执行输出顺序是否和我们模拟的执行时间相同,如下:

@Service
public class LoginLogServiceImpl extends ServiceImpl<LoginLogMapper, LoginLog> implements ILoginLogService {

    // 有返回值的异步方法CompletableFuture.supplyAsync()
    public CompletableFuture<String> test1() {
        return CompletableFuture.supplyAsync(() -> {
            ThreadUtil.sleep(1000);
            System.out.println("test1异步请求结果有返回");
            return "aaa";
        });
    }

    public CompletableFuture<String> test2() {
        return CompletableFuture.supplyAsync(() -> {
            ThreadUtil.sleep(6000);
            System.out.println("test2异步请求结果有返回");
            return "bbb";
        });
    }

    public CompletableFuture<String> test3() {
        return CompletableFuture.supplyAsync(() -> {
            ThreadUtil.sleep(3000);
            System.out.println("test3异步请求结果有返回");
            return "ccc";
        });
    }
}

 在controller层中利用CompletableFuture的get()方法获取数据,但是要加上try/catch捕获异常

@PostMapping("/login")
    public Result login() {

        System.out.println("登录验证成功");

        // 异步保存登录成功日志
        CompletableFuture<String> future1 = iLoginLogService.test1();
        CompletableFuture<String> future2 = iLoginLogService.test2();
        CompletableFuture<String> future3 = iLoginLogService.test3();

        System.out.println("模拟正常执行的方法");

        String result1 = "111";
        String result2 = "222";
        String result3 = "333";
        String result = "";
        
        // 需要用try/catch来获取异步返回值
        try {
            
            // get()方法获取返回值,并设置10秒超时就放弃,直接报错
            result1 = future1.get(10, TimeUnit.SECONDS);
            result2 = future2.get(10, TimeUnit.SECONDS);
            result3 = future3.get(10, TimeUnit.SECONDS);

            System.out.println("返回结果为:" + result1);
            System.out.println("返回结果为:" + result2);
            System.out.println("返回结果为:" + result3);

            result = result1 + result2 + result3;

            // 正常返回结果
            return Result.success(200, "登录成功", result);
        } catch (Exception e) {
            System.out.println(e);
            return Result.success(501, "登录失败,异常错误");
        }
    }

 结果可以看到如下图所示,异步线程同时执行,但最后返回结果是等所有线程执行完成后,再返回,这就是有回调的异步操作:

五、异步执行错误异常示例


    // 无回调
    @Async
    public CompletableFuture<Void> test() {
        return CompletableFuture.runAsync(() -> {

            // 模拟第三方请求加载时间
            Thread.sleep(1000);
        
            // 异常错误模拟
            Integer test = 1/0;

            System.out.println("异步请求开始执行完成");
        });
    }

 结果就是完全不影响接口执行,且打印出报错信息,如下:

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言中,实现多线程异步回调可以使用线程库和回调函数。以下是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> // 回调函数类型 typedef void (*callback_func)(void*); // 异步任务结构体 typedef struct { int data; callback_func callback; } async_task; // 异步任务处理函数 void* async_task_handler(void* arg) { async_task* task = (async_task*)arg; // 模拟耗时操作 sleep(3); // 执行回调函数 task->callback((void*)&(task->data)); free(task); pthread_exit(NULL); } // 回调函数示例 void callback(void* arg) { int* result = (int*)arg; printf("Callback: Result = %d\n", *result); } int main() { pthread_t thread; async_task* task = (async_task*)malloc(sizeof(async_task)); task->data = 10; task->callback = callback; // 创建新线程执行异步任务 pthread_create(&thread, NULL, async_task_handler, (void*)task); printf("Main thread: Waiting for callback...\n"); // 主线程等待异步任务完成 pthread_join(thread, NULL); printf("Main thread: Exiting...\n"); return 0; } ``` 在上述示例代码中,我们定义了一个`async_task`结构体,其中包含了一个整数数据和一个回调函数指针。`async_task_handler`函数是异步任务的处理函数,在其中执行耗时操作后会调用回调函数。`callback`函数是回调函数的示例,它接收处理结果作为参数,并在主线程中被调用。 在`main`函数中,我们创建了一个异步任务,并将其数据和回调函数设置好。然后,我们使用`pthread_create`函数创建了一个新线程执行异步任务。主线程在等待异步任务完成后,再输出相应的信息。 需要注意的是,多线程编程需要仔细处理线程间共享变量的同步与互斥访问,以避免数据竞争等问题。在实际应用中,可能需要更加复杂的线程管理和同步机制。以上只是一个简单的示例,供参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值