简单几步,实现异步新线程调用。
1、在主类中添加@EnableAsync注解:
- @SpringBootApplication
- @EnableScheduling
- @EnableAsync
- public class MySpringBootApplication {
- private static Logger logger = LoggerFactory.getLogger(MySpringBootApplication.class);
- public static void main(String[] args) {
- SpringApplication.run(MySpringBootApplication.class, args);
- logger.info("My Spring Boot Application Started");
- }
- }
2、创建一个AsyncTask类,在里面添加两个用@Async注解的task:
- /**
- * Asynchronous Tasks
- * @author Xu
- *
- */
- @Component
- public class AsyncTask {
- protected final Logger logger = LoggerFactory.getLogger(this.getClass());
- @Async
- public Future<String> doTask1() throws InterruptedException{
- logger.info("Task1 started.");
- long start = System.currentTimeMillis();
- Thread.sleep(5000);
- long end = System.currentTimeMillis();
- logger.info("Task1 finished, time elapsed: {} ms.", end-start);
- return new AsyncResult<>("Task1 accomplished!");
- }
- @Async
- public Future<String> doTask2() throws InterruptedException{
- logger.info("Task2 started.");
- long start = System.currentTimeMillis();
- Thread.sleep(3000);
- long end = System.currentTimeMillis();
- logger.info("Task2 finished, time elapsed: {} ms.", end-start);
- return new AsyncResult<>("Task2 accomplished!");
- }
- }
- public class TaskTests extends BasicUtClass{
- @Autowired
- private AsyncTask asyncTask;
- @Test
- public void AsyncTaskTest() throws InterruptedException, ExecutionException {
- Future<String> task1 = asyncTask.doTask1();
- Future<String> task2 = asyncTask.doTask2();
- while(true) {
- if(task1.isDone() && task2.isDone()) {
- logger.info("Task1 result: {}", task1.get());
- logger.info("Task2 result: {}", task2.get());
- break;
- }
- Thread.sleep(1000);
- }
- logger.info("All tasks finished.");
- }
- }
测试结果:
- 2016-12-13 11:12:24,850:INFO main (AsyncExecutionAspectSupport.java:245) - No TaskExecutor bean found for async processing
- 2016-12-13 11:12:24,864:INFO SimpleAsyncTaskExecutor-1 (AsyncTask.java:22) - Task1 started.
- 2016-12-13 11:12:24,865:INFO SimpleAsyncTaskExecutor-2 (AsyncTask.java:34) - Task2 started.
- 2016-12-13 11:12:27,869:INFO SimpleAsyncTaskExecutor-2 (AsyncTask.java:39) - Task2 finished, time elapsed: 3001 ms.
- 2016-12-13 11:12:29,866:INFO SimpleAsyncTaskExecutor-1 (AsyncTask.java:27) - Task1 finished, time elapsed: 5001 ms.
- 2016-12-13 11:12:30,853:INFO main (TaskTests.java:23) - Task1 result: Task1 accomplished!
- 2016-12-13 11:12:30,853:INFO main (TaskTests.java:24) - Task2 result: Task2 accomplished!
- 2016-12-13 11:12:30,854:INFO main (TaskTests.java:30) - All tasks finished.
可以看到,没有自定义的Executor,所以使用缺省的TaskExecutor 。
前面是最简单的使用方法。如果想使用自定义的Executor,可以按照如下几步来:
1、新建一个Executor配置类,顺便把@EnableAsync注解搬到这里来:
- @Configuration
- @EnableAsync
- public class ExecutorConfig {
- /** Set the ThreadPoolExecutor‘s core pool size. */
- private int corePoolSize = 10;
- /** Set the ThreadPoolExecutor‘s maximum pool size. */
- private int maxPoolSize = 200;
- /** Set the capacity for the ThreadPoolExecutor‘s BlockingQueue. */
- private int queueCapacity = 10;
- @Bean
- public Executor mySimpleAsync() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(corePoolSize);
- executor.setMaxPoolSize(maxPoolSize);
- executor.setQueueCapacity(queueCapacity);
- executor.setThreadNamePrefix("MySimpleExecutor-");
- executor.initialize();
- return executor;
- }
- @Bean
- public Executor myAsync() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(corePoolSize);
- executor.setMaxPoolSize(maxPoolSize);
- executor.setQueueCapacity(queueCapacity);
- executor.setThreadNamePrefix("MyExecutor-");
- // rejection-policy:当pool已经达到max size的时候,如何处理新任务
- // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- executor.initialize();
- return executor;
- }
- }
这里定义了两个不同的Executor,第二个重新设置了pool已经达到max size时候的处理方法;同时指定了线程名字的前缀。
2、自定义Executor的使用:
- /**
- * Asynchronous Tasks
- * @author Xu
- *
- */
- @Component
- public class AsyncTask {
- protected final Logger logger = LoggerFactory.getLogger(this.getClass());
- @Async("mySimpleAsync")
- public Future<String> doTask1() throws InterruptedException{
- logger.info("Task1 started.");
- long start = System.currentTimeMillis();
- Thread.sleep(5000);
- long end = System.currentTimeMillis();
- logger.info("Task1 finished, time elapsed: {} ms.", end-start);
- return new AsyncResult<>("Task1 accomplished!");
- }
- @Async("myAsync")
- public Future<String> doTask2() throws InterruptedException{
- logger.info("Task2 started.");
- long start = System.currentTimeMillis();
- Thread.sleep(3000);
- long end = System.currentTimeMillis();
- logger.info("Task2 finished, time elapsed: {} ms.", end-start);
- return new AsyncResult<>("Task2 accomplished!");
- }
- }
3、测试(测试用例不变)结果:
- 2016-12-13 10:57:11,998:INFO MySimpleExecutor-1 (AsyncTask.java:22) - Task1 started.
- 2016-12-13 10:57:12,001:INFO MyExecutor-1 (AsyncTask.java:34) - Task2 started.
- 2016-12-13 10:57:15,007:INFO MyExecutor-1 (AsyncTask.java:39) - Task2 finished, time elapsed: 3000 ms.
- 2016-12-13 10:57:16,999:INFO MySimpleExecutor-1 (AsyncTask.java:27) - Task1 finished, time elapsed: 5001 ms.
- 2016-12-13 10:57:17,994:INFO main (TaskTests.java:23) - Task1 result: Task1 accomplished!
- 2016-12-13 10:57:17,994:INFO main (TaskTests.java:24) - Task2 result: Task2 accomplished!
- 2016-12-13 10:57:17,994:INFO main (TaskTests.java:30) - All tasks finished.
- 2016-12-13 10:57:18,064 Thread-3 WARN Unable to register Log4j shutdown hook because JVM is shutting down. Using SimpleLogger
可见,线程名字的前缀变了,两个task使用了不同的线程池了。
源代码:https://github.com/xujijun/my-spring-boot
引言:Spring作为容器为我们托管对象,但是有时我们需要多线程执行任务,那么我们该如何配置呢?
解决:利用java的线程池Executor执行任务
步骤
1.配置TaskExecutor
这里直接将线程池注入
CorePoolSize代表执行任务的线程数量
public class TaskExecutorConfig implements AsyncConfigurer{//实现AsyncConfigurer接口 @Bean public Executor getAsyncExecutor() {//实现AsyncConfigurer接口并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,这样我们就获得了一个基于线程池TaskExecutor ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(5); taskExecutor.setMaxPoolSize(10); taskExecutor.setQueueCapacity(25); taskExecutor.initialize(); return taskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } }
这里我们得到了一个基于java的线程池Executer的线程池,然后设置了部分参数,返回了一个实例
2.编写我们需要执行的任务,并注明service
@Service @Slf4j public class AsyncTaskService { @Async public void dataTranslate(int i) { log.info("启动了线程"+i); } }
这里用日志打印
3.将线程池对象注入,并调用任务service。
最后在application中开始异步支持@EnableAsync
调用结果:可以看到,是不同的线程执行了打印任务,而且根据cpu时间片,抢占,可以看到线程执行顺序也发生了变化,说明是异步执行
总结:Spring Boot对多线程的支持和Spring没什么两样,就是需要先配置线程池,然后注入bean,再写异步方法,最后调用就可以了。
关于多线程中还有许多问题,如线程同步等就需要在写代码时多注意多思考了。另外,合理配置线程池参数也很重要