JAVA 使用@Async、Future、AsyncResult处理异步返回结果, 类似C# Task await功能

文章介绍了如何通过异步处理来优化用户登录接口的性能,详细阐述了使用@Async注解开启新线程以及Future对象进行结果获取的方法,指出异步处理能有效减少接口总耗时并提高并发吞吐量,但需注意线程池的合理配置以避免资源争抢。
摘要由CSDN通过智能技术生成

一、使用场景

以用户登录接口举例

1.验证账号密码, 成功 耗时 300ms

2.1.验证成功后,记录相关登录信息 耗时 100ms
2.2.验证成功后,获取系统内消息通知 耗时 300ms
2.3.验证成功后,获取首页推送内容 耗时 1000ms

3.返回、登录信息、消息通知、首页推送

如果接口中,每个步骤都是同步处理,总共就需要 1700s = 300ms + 100ms + 300ms + 1000ms

现在可以将步骤 2.1、 2.2、2.3, 开启异步线程,同时处理,(暂不考虑开启线程的耗时)
也就是意味步骤 2.1、 2.2、2.3,任务总耗时就是步骤2.3的耗时1000ms
再加上1步骤的验证耗时 300ms
即开启异步后,总共耗时 1300ms = 1000ms + 300ms, 足足减少了400ms

如果用户登录并发量大,异步处理可以大大提高吞吐量

注意:开启线程数量也不是越多越好,到达一定最大数量后,就只能等待已经开启的线程被释放,如果一个登录开启了大量线程,则会导致其他功能"无线程可用", 需要等待排队
这时可以自定义线程池,限制最大开启数量


二、实现简介

2.1. @Async

@Async 可以开启新线程,使用时需要在项目启动类添加 @EnableAsync
调用方和被调用方,不能在同一个类,否则不起作用

2.2 Future

Future.get()执行完成后, 返回结果; 调用时执行还没有完成,则会阻塞线程等待
Future.get(long timeout,TimeUnit unit)执行完成后, 返回结果; 设置等待超时时间
Future.cancel(boolean mayInterruptIfRunning) 停止任务,
如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败,返回false
调用cancel时,如果调用成功,而此任务尚未启动,则此任务将永不运行
如果任务正在执行,mayInterruptIfRunning 参数决定了是否向正在执行任务的线程发出中断
Future.isDone()是否执行完成
Future.isCancel()是否执行取消

2.3 AsyncResult 填充异步处理返回结果


三、代码示例

3.1异步任务

package com.aaa.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.util.concurrent.Future;

@Slf4j
@Component
public class TestHandler {

    @Async
    public Future<Integer> test(Integer i, Long millis){
        try {

            log.debug("异步测试-睡眠-开始, i:{}, 时间:{}", i, millis);
            Thread.sleep(millis);
            log.debug("异步测试-睡眠-结束, i:{}, 时间:{}", i, millis);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return new AsyncResult<>(i);
    }
}

3.1 调用

package com.aaa.controller;

import com.alibaba.fastjson.JSONObject;
import com.aaa.component.RobotExecuteHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.Future;

@Slf4j
@RestController
@RequestMapping("/v1/test")
public class TestController {

    @Autowired
    private TestHandler testHandler;

    @PostMapping("/asyncExecute")
    public Integer asyncExecute() {
        log.debug("异步测试-开始");

        Future<Integer> future1 =  testHandler.test(1,2000L);
        log.debug("异步测试-返回值 future1 :{}", JSONObject.toJSONString(future1));

        Future<Integer> future2 =  testHandler.test(2,1000L);
        log.debug("异步测试-返回值 future2:{}", JSONObject.toJSONString(future2));

        Future<Integer> future3 =  testHandler.test(3,4000L);
        log.debug("异步测试-返回值 future3:{}", JSONObject.toJSONString(future3));

        try {
            Integer i1 = future1.get();
            log.debug("异步测试-返回值future1 get:{}", JSONObject.toJSONString(i1));

            Integer i2 = future2.get();
            log.debug("异步测试-返回值future1 get:{}", JSONObject.toJSONString(i1));

            Integer i3 = future3.get();
            log.debug("异步测试-返回值future3 get:{}", JSONObject.toJSONString(i3));

            Integer sum = i1 + i2 + i3;
            log.debug("异步测试-结束, 求和结果:{}",sum);

            return sum;

        } catch (Exception e) {

            return -1;
        }
    }
}

3.1 注意

调用异步任务,应该是全部先调用, 后面获取结果
Future future1 = testHandler.test(1,2000L);
Future future2 = testHandler.test(2,1000L);
Future future3 = testHandler.test(3,4000L);

future1.get();
future2.get();
future3.get();

如果写成调用一个异步方法,紧跟着获取结果,主线程就会被阻塞, 这种写法是不可取的

Future future1 = testHandler.test(1,2000L);
future1.get();
Future future2 = testHandler.test(2,1000L);
future2.get();
Future future3 = testHandler.test(3,4000L);
future3.get();



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值