理念

为了快速响应浏览器,开启多线程执行任务

但是有一个缺点,会增加CPU资源的消耗,所以大的项目推荐使用MQ消息队列

编写代码:

  •  
@GetMapping("/addDB")    public String addDB() {        // 模拟数据交互        log.info("<01>");        sms();        log.info("<04>");        return "用户注册成功";    }
/** * 模拟发送短信 * * @return */ public String sms() { log.info("<02>"); try { log.info("正在发送短信..."); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return "短信发送完成"; }

 

这个接口最少需要4秒才能返回,如果发送短信时间更长,返回时间增加,这样用户的体验就非常不好

 

启动项目测试

 

SpringBoot之SpringBoot整合异步线程调用注解_java

 

 他是单线程去执行的,看过Tomcat的应该也知道,tomcat会为每一次请求从他的线程池中单独拿一个线程去执行,所以它是单线程 的

所以这里就可以使用Spring提供的异步注解

 

异步注解的使用

在方法上添加@Async注解

SpringBoot之SpringBoot整合异步线程调用注解_java_02

 

 并且在启动类中开启异步注解

SpringBoot之SpringBoot整合异步线程调用注解_java_03

 

启动测试:

SpringBoot之SpringBoot整合异步线程调用注解_java_04

 

 不对呀,这还是单线程呀

 

异步注解失效问题解决

需要把sms方法单独提取成一个类

SpringBoot之SpringBoot整合异步线程调用注解_java_05

 

package com.springboot.demo.async;
import lombok.extern.slf4j.Slf4j;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component;
/** * @author ZYGisComputer */@Slf4j@Componentpublic class SmsService {
/** * 模拟发送短信 * * @return */ @Async public String sms() { log.info("<02>"); try { log.info("正在发送短信..."); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return "短信发送完成"; } }

 

通过Autowirld的方式注入使用

SpringBoot之SpringBoot整合异步线程调用注解_java_06

 

 再次测试

SpringBoot之SpringBoot整合异步线程调用注解_java_07

 

 可以看到执行成功了,并且也是不同的线程,但是他这样都是每次都是new一个新的线程,这显然是不合理的,因为使用线程就应该考虑采用线程池

 

异步注解整合线程池

创建config包,并在下面创建ThreadPoolConfig.java

SpringBoot之SpringBoot整合异步线程调用注解_java_08

 

 

package com.springboot.demo.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.task.TaskExecutor;import org.springframework.scheduling.annotation.EnableAsync;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration@EnableAsyncpublic class ThreadPoolConfig {
/** * 每秒需要多少个线程处理? * tasks/(1/taskcost) */ private int corePoolSize = 3;
/** * 线程池维护线程的最大数量 * (max(tasks)- queueCapacity)/(1/taskcost) */ private int maxPoolSize = 3;
/** * 缓存队列 * (coreSizePool/taskcost)*responsetime */ private int queueCapacity = 10;
/** * 允许的空闲时间 * 默认为60 */ private int keepAlive = 100;
@Bean public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 设置核心线程数 executor.setCorePoolSize(corePoolSize); // 设置最大线程数 executor.setMaxPoolSize(maxPoolSize); // 设置队列容量 executor.setQueueCapacity(queueCapacity); // 设置允许的空闲时间(秒) //executor.setKeepAliveSeconds(keepAlive); // 设置默认线程名称 executor.setThreadNamePrefix("thread-"); // 设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务 // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 等待所有任务结束后再关闭线程池 executor.setWaitForTasksToCompleteOnShutdown(true); return executor; }
}

 

在@Async注解中配置线程池名称就可以

SpringBoot之SpringBoot整合异步线程调用注解_java_09

 

 启动项目测试:

 

2021-01-26 18:18:53,820 [http-nio-8082-exec-1] INFO (HelloService.java:76)- <01>2021-01-26 18:18:53,823 [http-nio-8082-exec-1] INFO (HelloService.java:78)- <04>2021-01-26 18:18:53,823 [http-nio-8082-exec-1] INFO (LogAspect.java:50)- RESPONSE : 用户注册成功2021-01-26 18:18:53,829 [thread-1] INFO (SmsService.java:24)- <02>2021-01-26 18:18:53,831 [thread-1] INFO (SmsService.java:26)- 正在发送短信...<执行定时任务>:18:18:56<执行定时任务>:18:19:012021-01-26 18:19:01,340 [http-nio-8082-exec-3] INFO (LogAspect.java:37)- URL : http://localhost:8082/addDB2021-01-26 18:19:01,341 [http-nio-8082-exec-3] INFO (LogAspect.java:38)- HTTP_METHOD : GET2021-01-26 18:19:01,341 [http-nio-8082-exec-3] INFO (LogAspect.java:39)- IP : 0:0:0:0:0:0:0:12021-01-26 18:19:01,341 [http-nio-8082-exec-3] INFO (HelloService.java:76)- <01>2021-01-26 18:19:01,342 [http-nio-8082-exec-3] INFO (HelloService.java:78)- <04>2021-01-26 18:19:01,342 [thread-2] INFO (SmsService.java:24)- <02>2021-01-26 18:19:01,342 [thread-2] INFO (SmsService.java:26)- 正在发送短信...2021-01-26 18:19:01,342 [http-nio-8082-exec-3] INFO (LogAspect.java:50)- RESPONSE : 用户注册成功2021-01-26 18:19:01,922 [http-nio-8082-exec-4] INFO (LogAspect.java:37)- URL : http://localhost:8082/addDB2021-01-26 18:19:01,922 [http-nio-8082-exec-4] INFO (LogAspect.java:38)- HTTP_METHOD : GET2021-01-26 18:19:01,923 [http-nio-8082-exec-4] INFO (LogAspect.java:39)- IP : 0:0:0:0:0:0:0:12021-01-26 18:19:01,923 [http-nio-8082-exec-4] INFO (HelloService.java:76)- <01>2021-01-26 18:19:01,923 [http-nio-8082-exec-4] INFO (HelloService.java:78)- <04>2021-01-26 18:19:01,924 [thread-3] INFO (SmsService.java:24)- <02>2021-01-26 18:19:01,924 [http-nio-8082-exec-4] INFO (LogAspect.java:50)- RESPONSE : 用户注册成功2021-01-26 18:19:01,924 [thread-3] INFO (SmsService.java:26)- 正在发送短信...2021-01-26 18:19:02,559 [http-nio-8082-exec-2] INFO (LogAspect.java:37)- URL : http://localhost:8082/addDB2021-01-26 18:19:02,559 [http-nio-8082-exec-2] INFO (LogAspect.java:38)- HTTP_METHOD : GET2021-01-26 18:19:02,559 [http-nio-8082-exec-2] INFO (LogAspect.java:39)- IP : 0:0:0:0:0:0:0:12021-01-26 18:19:02,559 [http-nio-8082-exec-2] INFO (HelloService.java:76)- <01>2021-01-26 18:19:02,560 [thread-1] INFO (SmsService.java:24)- <02>2021-01-26 18:19:02,560 [http-nio-8082-exec-2] INFO (HelloService.java:78)- <04>2021-01-26 18:19:02,560 [thread-1] INFO (SmsService.java:26)- 正在发送短信...2021-01-26 18:19:02,560 [http-nio-8082-exec-2] INFO (LogAspect.java:50)- RESPONSE : 用户注册成功

 

可以看到使用完成thread-3之后使用的又是thread-1了,线程池的参数可以根据自己项目的实际情况调整,不懂线程池的可以去看看我写的《线程池理念分析及其手写》

到此整合异步注解完成