【多线程】@Async注解和线程池
@Async注解和线程池
@Async
1.@Async是什么
Spring⾃带了⽀持任务调度和异步⽅法调⽤的注解,在需要被调⽤的⽅法上加上@Async注解,则该⽅法会被异步调⽤2.@Async的使⽤
步骤⼀:在启动类上⾯加@EnableAsync注解
@EnableAsync
@SpringBootApplication
public class OperationApplication {
/**
* 启动类
*/
public static void main(String[] args){
SpringApplication.run(OperationApplication.class, args);
}
}
或者
创建⼀个配置类,在配置类上⾯加@EnableAsync注解
@Configuration
@EnableAsync
public class ThreadPoolConfig {
}
注意:@Async默认启动的线程池,属性
步骤⼆:在对应的⽅法上加@Async注解
@Async
public void method(){
log.info("执⾏该线程的名字为:"+ Thread.currentThread().getName());
}
3.@Async的使⽤原理
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interface Async {
/
**
* A qualifier value for the specified asynchronous operation(s).
* <p>May be ud to determine the target executor to be ud when executing
* the asynchronous operation(s), matching the qualifier value (or the bean
* name) of a specific {@link urrent.Executor Executor} or
* {@link ask.TaskExecutor TaskExecutor}
* bean definition.
* <p>When specified on a class-level {@code @Async} annotation, indicates that the * given executor should be ud for all methods within the class. Method-level u * of {@code Async#value} always overrides any value t at the class level.
* @since 3.1.2
*/
String value()default"";
}
剖析注释可以发现
@Async 注解中有⼀个 value 属性,看注释应该是可以指定是哪个线程池的写⼀个demo
相关的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
启动类
@EnableAsync// 开启异步注解
@SpringBootApplication
public class ThreadDemoApplication {
public static void main(String[] args){
SpringApplication.run(ThreadDemoApplication.class, args);
}
}
调⽤的⽅法
@Slf4j
@Service
public class AsyncService {
@Async
public void method(){
log.info("执⾏该线程的名字为:"+ Thread.currentThread().getName());
}
}
写⼀个调⽤异步⽅法的接⼝
@Slf4j
@RestController
public class TestController {
@Resource
private AsyncService asyncService;
@GetMapping("/test")
public void test(){
log.info("执⾏test⽅法的线程的名字为:"+ Thread.currentThread().getName());
}
}
调⽤结果
INFO 22864---[ task-1]AsyncService :执⾏该线程的名字为:task-1为了⼀探默认线程池的配置,就⾏⿊盒压测⼀下
@Slf4j
@RestController
public class TestController {
@Resource
private AsyncService asyncService;
@GetMapping("/test")
public void test(@RequestParam("num") Integer num){
log.info("执⾏test⽅法的线程的名字为:"+ Thread.currentThread().getName());
for(int i =0; i < num; i++){
}
}
}
不多废话,直接进⾏100000次异步调⽤
localhost:8080/test?num=100000
发现控制台并未报错,单堆内存⼏乎要爆炸,怀疑可能是⼀个⽆界队列,任务有可能都进队列⾥⾯排队了,导致内存飙升
默认的线程池有导致内存溢出的风险。
可以通过创建⾃定义线程池代替默认线程池
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor(){
ThreadPoolTaskExecutor taskExecutor =new ThreadPoolTaskExecutor();
taskExecutor.tCorePoolSize(10);
taskExecutor.tMaxPoolSize(50);
taskExecutor.tQueueCapacity(200);
taskExecutor.tKeepAliveSeconds(60);
taskExecutor.tThreadNamePrefix("Thread-");
taskExecutor.tAwaitTerminationSeconds(60);
return taskExecutor;
}
}
控制台
2021-12-2418:18:38.014 INFO 10688---[ Thread-30]AsyncService :执⾏该线程的名字为:Thread-30
2021-12-2418:18:38.014 INFO 10688---[ Thread-2]AsyncService :执⾏该线程的名字为:Thread-2
2021-12-2418:18:38.014 INFO 10688---[ Thread-8]AsyncService :执⾏该线程的名字为:Thr
ead-8
2021-12-2418:18:38.014 INFO 10688---[ Thread-15]AsyncService :执⾏该线程的名字为:Thread-15
2021-12-2418:18:38.014 INFO 10688---[ Thread-25]AsyncService :执⾏该线程的名字为:Thread-25
2021-12-2418:18:38.014 INFO 10688---[ Thread-5]AsyncService :执⾏该线程的名字为:Thread-5
2021-12-2418:18:38.014 INFO 10688---[ Thread-31]AsyncService :执⾏该线程的名字为:Thread-31
2021-12-2418:18:38.014 INFO 10688---[ Thread-24]AsyncService :执⾏该线程的名字为:Thread-24
2021-12-2418:18:38.014 INFO 10688---[ Thread-22]AsyncService :执⾏该线程的名字为:Thread-22
2021-12-2418:18:38.014 INFO 10688---[ Thread-9]AsyncService :执⾏该线程的名字为:Thread-9
2021-12-2418:18:38.022 ERROR 10688---[nio-8080-exec-1]C.[.[.[/].[dispatcherServlet]: Servlet.rvice()for rvlet [dispatcherServlet] in conte
xt with[] threw exception [Request processing failed; nested exception is TaskRejectedException: Executor [ ThreadPoolExecutor@44a62e23[Running, pool size =50, active threads =50, queued tasks =91, completed tasks =1001]] did not accept task: AsyncExecutionInterceptor$$Lambda$580/2138858786@265a267e]with cau
RejectedExecutionException: Task FutureTask@293459fe rejected from ThreadPoolExecutor@ 44a62e23[Running, pool size =50, active threads =50, queued tasks =129, completed tasks =961]
at jectedExecution(ThreadPoolExecutor.java:2063)~[na:1.8.0_162]
ject(ThreadPoolExecutor.java:830)~[na:1.8.0_162]
ute(ThreadPoolExecutor.java:1379)~[na:1.8.0_162]
at AbstractExecutorService.submit(AbstractExecutorService.java:134)~[na:1.8.0_162]
说明 @Async使⽤了我们⾃定义的线程池,当超出最⼤线程数且队列满的时候,通过默认拒绝策略new
ThreadPoolExecutor.AbortPolicy()丢弃任务并抛出RejectedExecutionException异常
问题1:当不指定的时候,默认线程池是什么?具体配置是什么?看源码
value属性被使⽤的地⽅就⼀个,⾛