SpingBoot异步执行

异步任务常用场景

  • 发送消息
  • 发送邮件
  • App消息推送
  • 节省运维凌晨发布任务时间提供效率

示例:
springboot启动类加上@EnableAsync注解,异步线程任务开关
异步任务:

@Component
public class TestAsyncTask {

    @Async
    public Future<String> doTask1() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(1000);
        long end = System.currentTimeMillis();
        System.out.println("任务 1 耗时:" + (end - start) + " ms");
        return new AsyncResult<>("666");
    }

    @Async
    public Future<String> doTask2() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(800);
        long end = System.currentTimeMillis();
        System.out.println("任务 2 耗时:" + (end - start) + " ms");
        return new AsyncResult<>("666");
    }

    @Async
    public Future<String> doTask3() throws Exception {
        long start = System.currentTimeMillis();
        Thread.sleep(600);
        long end = System.currentTimeMillis();
        System.out.println("任务 3 耗时:" + (end - start) + " ms");
        return new AsyncResult<>("666");
    }
}

执行异步任务

@RestController
@RequestMapping("task")
public class DoTask {

    @Autowired
    private TestAsyncTask asyncTask;

    @RequestMapping("testAsync")
    public String execAsyncTask() throws Exception {

        long start = System.currentTimeMillis();

        Future<String> a = asyncTask.doTask1();
        Future<String> b = asyncTask.doTask2();
        Future<String> c = asyncTask.doTask3();

        while (true) {
            if (a.isDone() && b.isDone() && c.isDone()) {
                break;
            }
        }

        long end = System.currentTimeMillis();

        String times = "任务全部完成,总耗时:" + (end - start) + " ms";
        System.out.println(times);
        System.out.println(a);
        return times;
    }
}

输出结果
是大多数
异步执行用时少于同步执行

异步方法与同步方法执行过程
TestService

@Service
public class TestService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Async
    public void asyncMethod() {
        sleep();
        logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
    }

    public void syncMethod() {
        sleep();
    }

    private void sleep() {
        try {
            logger.info("睡眠开始");
            TimeUnit.SECONDS.sleep(2);
            logger.info("睡眠结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

TestController

@RestController
public class TestController {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private TestService testService;

    @GetMapping("async")
    public void testAsync() {
        long start = System.currentTimeMillis();
        logger.info("异步方法开始");

        testService.asyncMethod();

        logger.info("异步方法结束");
        long end = System.currentTimeMillis();
        logger.info("总耗时:{} ms", end - start);
    }

    @GetMapping("sync")
    public void testSync() {
        long start = System.currentTimeMillis();
        logger.info("同步方法开始");

        testService.syncMethod();

        logger.info("同步方法结束");
        long end = System.currentTimeMillis();
        logger.info("总耗时:{} ms", end - start);
    }
}

访问http://localhost:8080/async同步执行结果:

15:29:44.559  INFO 5476 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 同步方法开始
15:29:44.577  INFO 5476 --- [nio-8080-exec-1] com.example.demo.service.TestService     : 睡眠开始
15:29:46.579  INFO 5476 --- [nio-8080-exec-1] com.example.demo.service.TestService     : 睡眠结束
15:29:46.580  INFO 5476 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 同步方法结束
15:29:46.580  INFO 5476 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 总耗时:2021 ms

访问http://localhost:8080/sync,异步执行结果

15:29:58.169  INFO 5476 --- [nio-8080-exec-3] c.e.demo.controller.TestController       : 异步方法开始
15:29:58.186  INFO 5476 --- [nio-8080-exec-3] c.e.demo.controller.TestController       : 异步方法结束
15:29:58.187  INFO 5476 --- [nio-8080-exec-3] c.e.demo.controller.TestController       : 总耗时:18 ms
15:29:58.195  INFO 5476 --- [         task-1] com.example.demo.service.TestService     : 睡眠开始
15:30:00.196  INFO 5476 --- [         task-1] com.example.demo.service.TestService     : 睡眠结束
15:30:00.196  INFO 5476 --- [         task-1] com.example.demo.service.TestService     : 异步方法内部线程名称:task-1

弊端:每次执行异步任务都需要新建一个线程。每次线程的创建与销毁会造成不必要的开销。可使用自定义线程池

添加线程池配置类
@Configuration
public class AsyncPoolConfig {

    @Bean
    public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数,默认为1
        executor.setCorePoolSize(20);
        //线程池最大线程数。只有当核心线程都被用完并且缓冲队列满后,才会开始申超过请核心线程数的线程,默认值为Integer.MAX_VALUE
        executor.setMaxPoolSize(200);
        //缓冲队列
        executor.setQueueCapacity(25);
        //超出核心线程数的线程的空闲时候的最大存活时间
        executor.setKeepAliveSeconds(200);
        //线程名字前缀
        executor.setThreadNamePrefix("asyncThread");
        //是否等待全部线程执行完毕才关闭线程池。默认false
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //setWaitForTasksToCompleteOnShutdown的等待时长,默认为0,不等待
        executor.setAwaitTerminationSeconds(60);
        //线程的处理策略 默认为abortPolicy
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        executor.initialize();
        return executor;
    }
}

修改TestService
@async加上线程池名称
@Async("asyncThreadPoolTaskExecutor")
再次运行http://localhost:8080/async ,结果:

15:45:56.975  INFO 1340 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 异步方法开始
15:45:56.987  INFO 1340 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 异步方法结束
15:45:56.988  INFO 1340 --- [nio-8080-exec-1] c.e.demo.controller.TestController       : 总耗时:12 ms
15:45:57.003  INFO 1340 --- [   asyncThread1] com.example.demo.service.TestService     : 睡眠开始
15:45:59.005  INFO 1340 --- [   asyncThread1] com.example.demo.service.TestService     : 睡眠结束
15:45:59.005  INFO 1340 --- [   asyncThread1] com.example.demo.service.TestService     : 异步方法内部线程名称:asyncThread1

异步回调

若异步方法有返回值,可使用Future接收回调值

@Async("asyncThreadPoolTaskExecutor")
public Future<String> asyncMethod() {
    sleep();
    logger.info("异步方法内部线程名称:{}", Thread.currentThread().getName());
    return new AsyncResult<>("hello async");
}

AsyncResult为Spring实现的Future实现类
在这里插入图片描述

@GetMapping("async")
public String testAsync() throws Exception {
    long start = System.currentTimeMillis();
    logger.info("异步方法开始");

    Future<String> stringFuture = testService.asyncMethod();
    String result = stringFuture.get();
    logger.info("异步方法返回值:{}", result);
    
    logger.info("异步方法结束");

    long end = System.currentTimeMillis();
    logger.info("总耗时:{} ms", end - start);
    return stringFuture.get();
}

通过future的get方法获取异步调用的返回值

在这里插入图片描述
通过返回结果我们可以看出Future的get方法为阻塞方法,只有当异步方法返回内容了,程序才会继续往下执行。get还有一个get(long timeout, TimeUnit unit)重载方法,我们可以通过这个重载方法设置超时时间,即异步方法在设定时间内没有返回值的话,直接抛出java.util.concurrent.TimeoutException异常。
比如设置超时时间为60秒:
String result = stringFuture.get(60, TimeUnit.SECONDS);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,您可以通过使用异步执行来提高应用程序的性能和响应能力。Spring Boot提供了多种方式来实现异步执行,下面是其中几种常用的方法: 1. 使用@Async注解:在Spring Boot中,可以使用@Async注解将方法标记为异步执行。首先,在主类上添加@EnableAsync注解以启用异步执行功能。然后,在需要异步执行的方法上添加@Async注解。这样,当调用该方法时,Spring会将其放入线程池中并立即返回,而不会阻塞主线程。 ```java @SpringBootApplication @EnableAsync public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } } ``` ```java @Service public class YourService { @Async public void yourAsyncMethod() { // 异步执行的逻辑 } } ``` 2. 使用CompletableFuture:CompletableFuture是Java 8中引入的一个异步编程工具类,可以很方便地实现异步执行。在Spring Boot中,您可以将CompletableFuture与@Async一起使用来实现异步执行。 ```java @Service public class YourService { @Async public CompletableFuture<Result> yourAsyncMethod() { // 异步执行的逻辑 return CompletableFuture.completedFuture(result); } } ``` 3. 使用ThreadPoolTaskExecutor:如果您需要更多的控制权,您可以自定义一个ThreadPoolTaskExecutor来管理异步任务的线程池。在应用程序配置文件中进行配置,然后在需要异步执行的方法上使用@Async注解。 ```java @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(30); executor.initialize(); return executor; } } ``` ```java @Service public class YourService { @Async public void yourAsyncMethod() { // 异步执行的逻辑 } } ``` 通过以上方法,您可以在Spring Boot应用程序中实现异步执行,并提升应用程序的性能和响应能力。请根据您的具体需求选择适合的方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值