所有分类
  • 所有分类
  • 未分类

Spring–@Async异步执行的方法

简介

本文介绍Spring的@Async的用法。@Async是用来异步执行任务的。

基础代码

正常情况下,执行两个任务是这样的:

Controller

package com.knife.example.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@Api(tags = "测试")
@RequestMapping("test")
@RestController
public class HelloController {
    @Autowired
    private HelloService helloService;

    @ApiOperation("测试1")
    @PostMapping("test1")
    public void test() {
        helloService.task1();
        helloService.task2();
    }
}

Service

package com.knife.example.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class HelloService {
    public void task1() {
        log.info("开始任务1");
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.info("完成任务1,耗时:" + (end - start) + "毫秒");
    }

    public void task2() {
        log.info("开始任务2");
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.info("完成任务2,耗时:" + (end - start) + "毫秒");
    }
}

它的结果是:是顺序执行的。

2024-07-07 15:53:00.067  INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService  : 开始任务1
2024-07-07 15:53:03.067  INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService  : 完成任务1,耗时:3000毫秒
2024-07-07 15:53:03.067  INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService  : 开始任务2
2024-07-07 15:53:06.073  INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService  : 完成任务2,耗时:3006毫秒

使用异步加快速度

假如上边task1和task2是不相关的,那完全可以同时运行,加快速度!下边就用异步来做:

1.启用异步(@EnableAsync)

在启动类上加@EnableAsync

package com.knife.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

2.将方法标记为@Async

@Async也可以放到类上边,表示所有方法都是异步执行。

下边方法上都加了@Async。另外,为了等待任务执行结束,用了CompletableFuture。

package com.knife.example.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

@Slf4j
@Component
public class HelloService {
    @Async
    public CompletableFuture<String> task1() {
        log.info("开始任务1");
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.info("完成任务1,耗时:" + (end - start) + "毫秒");

        return CompletableFuture.completedFuture("任务1完成");
    }

    @Async
    public CompletableFuture<String> task2() {
        log.info("开始任务2");
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.info("完成任务2,耗时:" + (end - start) + "毫秒");

        return CompletableFuture.completedFuture("任务2完成");
    }
}

测试:速度变快了,因为使用了两个线程去执行。

2024-07-07 15:59:33.264  INFO 8708 --- [         task-1] c.knife.example.controller.HelloService  : 开始任务1
2024-07-07 15:59:33.264  INFO 8708 --- [         task-2] c.knife.example.controller.HelloService  : 开始任务2
2024-07-07 15:59:36.270  INFO 8708 --- [         task-2] c.knife.example.controller.HelloService  : 完成任务2,耗时:3006毫秒
2024-07-07 15:59:36.270  INFO 8708 --- [         task-1] c.knife.example.controller.HelloService  : 完成任务1,耗时:3006毫秒
2024-07-07 15:59:36.270  INFO 8708 --- [nio-8080-exec-1] c.k.example.controller.HelloController   : 任务完成,总耗时:3016毫秒

Async的配置

applicaion.yml

spring:
  task:
    execution:
      pool:
        # 核心线程数。默认是8
        core-size: 3
        # 最大线程数。默认是整数最大值
        max-size: 10
        # 队列长度。默认是整数最大值
        queue-capacity: 100
        # 是否允许核心线程超时。默认是true
        allow-core-thread-timeout: true
        # 空闲线程存活时间。默认是60s
        keep-alive: 60s
      shutdown:
        # 退出时等待线程结束。默认是false
        await-termination: false
        # 等待线程结束的时间
        await-termination-period: 30s
      # 线程名称前缀。默认是task-
      thread-name-prefix: task-

自定义线程池

package com.knife.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
public class TaskExecutorConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(100);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

注意事项

@Async是动态代理实现的,如果调用同一个类里被@Async标记了的方法,需要先注入当前类,再去调用。例如:

package com.knife.example.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.CompletableFuture;

@Slf4j
@Api(tags = "测试")
@RequestMapping("test")
@RestController
public class HelloController {
    @Autowired
    private HelloController helloController;
    
    @ApiOperation("测试1")
    @PostMapping("test1")
    public void test() {
        long start = System.currentTimeMillis();

        CompletableFuture<String> task1 = helloController.task1();
        CompletableFuture<String> task2 = helloController.task2();
        CompletableFuture.allOf(task1, task2).join();

        long end = System.currentTimeMillis();

        log.info("任务完成,总耗时:" + (end - start) + "毫秒");
    }

    @Async
    public CompletableFuture<String> task1() {
        log.info("开始任务1");
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.info("完成任务1,耗时:" + (end - start) + "毫秒");

        return CompletableFuture.completedFuture("任务1完成");
    }

    @Async
    public CompletableFuture<String> task2() {
        log.info("开始任务2");
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        log.info("完成任务2,耗时:" + (end - start) + "毫秒");

        return CompletableFuture.completedFuture("任务2完成");
    }
}
0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录