一些重要的后台接口,希望加快任务的执行速度,可以考虑 多线程异步执行任务。
@Async是spring框架中十分好用的异步写法:
-
在方法上使用该@Async注解,申明该方法是一个异步任务;
-
在类上面使用该@Async注解,申明该类中的所有方法都是异步任务;
-
使用此注解的方法的类对象,必须是spring管理下的bean对象;
-
要想使用异步任务,需要在主类上开启异步配置,即,配置上@EnableAsync注解;
一、SpringBoot使用@Async注解步骤
step1、启动类上使用@EnableAsync注解
@SpringBootApplication
@EnableAsync
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
step2、异步方法所在的类注入容器中
@Componetpublic class Wtt{}
除了@Componet,也可以是@Controller、@RestController、@Service、@Configuration等注解,加入到Ioc容器里。
step3、方法上加上@Async注解
@Servicepublic class Wtt{@Asyncpublic void a() {}
}
二、@Async异步失效 的 两种情况
因为@Async
是基于 Aop思想 实现的,所有下面两种情况也会失效。
1、异步方法使用 static 修饰
@Asyncpublic static void a() {}
2、调用方法 和 异步方法 在同一个类中
当异步方法和调用方法在同一个类中时,是没办法通过Ioc里的bean来执行异步方法的,从而变成同步方法。
如下:
@Component
public class Task {/*** 调异步方法和异步方法在同一个类 @Async执行失败*/public void dotask() {this.taskOne();this.taskTwo();}@Asyncpublic void taskOne() {//执行任务1}@Asyncpublic void taskTwo() {//执行任务2}
}
三、@Async + 自定义线程池
如果 不自定义异步方法 的 线程池, 那么 默认使用 SimpleAsyncTaskExecutor线程池。
SimpleAsyncTaskExecutor
:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。因此 并发大的时候会产生严重的性能问题。
Spring 推荐 使用 ThreadPoolTaskExecutor
类来创建线程池。
因此 在 SpringBoot 2.0.9 版本及以前,@Async 默认使用的是 SimpleAsyncTaskExecutor;
从 2.1.0 开始,@Async 默认使用的是 ThreadPoolTaskExecutor。
也就是说新版本,其实spring boot默认使用的已经是 ThreadPoolTaskExecutor 线程池了,大家不用再去手动更改默认的线程池,
不过还是可以在 配置文件 更改 ThreadPoolTaskExecutor 的参数。
自定义线程池
package com.wtt.components;import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;@Configuration
public class Whero {@Bean(name = "wheroCreateNewAsyncExecutor")public Executor newAsync() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();//设置核心线程数taskExecutor.setCorePoolSize(10);// 线程池维护线程的最大数量,只有在缓冲队列满了以后才会申请超过核心线程数的线程taskExecutor.setMaxPoolSize(100);//缓存队列taskExecutor.setQueueCapacity(50);//允许的空闲时间,当超过了核心线程数之外的线程在空闲时间到达之后会被销毁taskExecutor.setKeepAliveSeconds(200);//异步方法内部线程名称taskExecutor.setThreadNamePrefix("whero-AsyncExecutor");//拒绝策略taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());taskExecutor.initialize();return taskExecutor;}
}
- 异步任务
package com.wtt.components;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;@Component
@Slf4j
public class Wtt {@Async("wheroCreateNewAsyncExecutor")public Future<String> taskOne() throws Exception {log.info("任务1线程名称 = {}", Thread.currentThread().getName());return new AsyncResult<>("1完成");}@Async("wheroCreateNewAsyncExecutor")public Future<String> taskTwo() throws Exception {log.info("任务2线程名称 = {}", Thread.currentThread().getName());return new AsyncResult<>("2完成");}@Asyncpublic Future<String> taskThere() throws Exception {log.info("任务3线程名称 = {}", Thread.currentThread().getName());return new AsyncResult<>("执行任务3完成");}
}
- 测试 执行 异步任务
package com.wtt.controller;import com.wtt.components.Wtt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@Slf4j
public class UsersController {@Autowiredprivate Wtt wtt;@GetMapping("/a1")public int a1() throws Exception {long start = System.currentTimeMillis();wtt.taskOne();wtt.taskTwo();wtt.taskThere();long end = System.currentTimeMillis();log.info("总任务执行结束,总耗时={} 毫秒", end - start);return 0;}
}
输出:
[nio-8886-exec-1] com.wtt.controller.UsersController : 总任务执行结束,总耗时=11 毫秒
[-AsyncExecutor2] com.wtt.components.Wtt : 任务2线程名称 = whero-AsyncExecutor2
[-AsyncExecutor1] com.wtt.components.Wtt : 任务1线程名称 = whero-AsyncExecutor1
[-AsyncExecutor3] com.wtt.components.Wtt : 任务3线程名称 = whero-AsyncExecutor3
虽然 我们设定的 任务1 和 任务2 配置走 自定义的线程池,任务3 还是走 默认线程池, 但是我们发现 三个 任务走的都是 我们自定义的 线程池。
@Async 是 方法级别 的 异步, 而 CompletableFuture 是 代码级别 的 异步,所以 很多场景下 还是推荐使用 CompletableFuture 进行 异步操作。