1 使用场景
在Spring Boot项目中,有时候需要单独监控某一个业务,或者需要在主流程之外单独处理一些数据。
在Spring项目中,由于Bean 对象是spring容器管理的,直接new出来的对象是没法使用bean里面依赖的其他组件,如dao,mapper等。此时,如果想使用bean里依赖的其他组件,则需要spring的多线程。
此篇文章,只是介绍了使用spring多线程的简单方式。至于多线程的参数设置,自定义线程池等将在另外的文章详解。
2 实现方式
2.1 使用Spring封装的异步
2.1.1 介绍
Spring Boot开启异步,需要两个注解:@EnableAsync和@Async。
-
@EnableAsync
在配置类中通过添加该注解,开启对异步任务的支持。
-
@Async
在实际执行的bean的方法中使用该注解表名这是一个异步任务。
2.1.2 样例
2.1.2.1 目录结构
Study
├── src
│ ├── main
│ │ ├── java
│ │ │ └── top
│ │ │ └── yxdz
│ │ │ └── study
│ │ │ ├── StudyApplication.java
│ │ │ └── spring
│ │ │ └── springboot
│ │ │ └── thread
│ │ │ └── service
│ │ │ ├── ITestService.java
│ │ │ └── impl
│ │ │ └── TestSerivceImpl.java
│ └── test
│ └── java
│ └── top
│ └── yxdz
│ └── study
│ └── StudyApplicationTests.java
2.1.2.2 代码
-
StudyApplicationTests
——测试入口这是测试类,也是配置类,添加了注解
@EnableAsync
,用来在此测试中启用异步。package top.yxdz.study; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.test.context.junit4.SpringRunner; import top.yxdz.study.spring.springboot.thread.service.ITestService; @RunWith(SpringRunner.class) @SpringBootTest @EnableAsync public class StudyApplicationTests { @Autowired ITestService iTestService; @Test public void contextLoads() { for(int i=0; i<5; i++){ iTestService.method1("Async" + i); } try { //等待10s,防止异步代码被强制关闭导致线程抛出异常 Thread.sleep(10000); }catch (InterruptedException e){ e.printStackTrace(); } } }
-
ITestService
——测试类接口package top.yxdz.study.spring.springboot.thread.service; public interface ITestService { /** * 异步测试 * @param msg */ void method1(String msg); }
-
TestSerivceImpl
——测试类实现这是实现类,在实现函数上添加了注释
@Async
表示异步调用。import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import top.yxdz.study.spring.springboot.thread.service.ITestService; import java.time.LocalDateTime; @Service("TestSerivceImpl") public class TestSerivceImpl implements ITestService { private static Logger LOG = LoggerFactory.getLogger(TestSerivceImpl.class); @Override @Async public void method1(String msg){ try { LOG.info(LocalDateTime.now().toString() + msg); Thread.sleep(1000); LOG.info(LocalDateTime.now().toString() + msg); }catch (InterruptedException e){ e.printStackTrace(); } } }
2.1.2.3 执行结果
-
执行结果
2018-12-12 20:07:37.631 INFO 34428 --- [ task-5] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:37.631Async4 2018-12-12 20:07:37.631 INFO 34428 --- [ task-1] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:37.631Async0 2018-12-12 20:07:37.631 INFO 34428 --- [ task-4] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:37.631Async3 2018-12-12 20:07:37.631 INFO 34428 --- [ task-2] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:37.631Async1 2018-12-12 20:07:37.631 INFO 34428 --- [ task-3] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:37.631Async2 2018-12-12 20:07:38.635 INFO 34428 --- [ task-4] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:38.635Async3 2018-12-12 20:07:38.635 INFO 34428 --- [ task-2] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:38.635Async1 2018-12-12 20:07:38.635 INFO 34428 --- [ task-3] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:38.635Async2 2018-12-12 20:07:38.635 INFO 34428 --- [ task-1] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:38.635Async0 2018-12-12 20:07:38.635 INFO 34428 --- [ task-5] t.y.s.s.s.t.s.impl.TestSerivceImpl : 2018-12-12T20:07:38.635Async4
-
结果说明
该方式使用的是异步,执行
iTestService.method1("Async" + i);
时,会开一个线程执行,并不会影响主线程的运行,for
循环会毫无障碍(不会受Thread.sleep(1000)
的影响)运行完毕,同时也会产生四个线程,独立运行method1
方法。因为是四个线程独立运行的结果,所以结果的顺序并不能保证每次一致。