在我们的应用系统中,经常会处理一些耗时任务,自然而然的会想到使用多线程。JDK给我们提供了非常方便的操作线程的API,JDK5之后更是新增了JUC包的支持,并发编程大师Doug Lea(JDK并发的作者)也是一直在为我们使用线程做着不懈的努力。
为什么还要使用Spring来实现多线程呢?这是句废话!实际有两个原因,第一使用Spring比使用JDK原生的并发API更简单。第二我们的应用环境一般都会集成Spring,我们的Bean也都交给Spring来进行管理,那么使用Spring来实现多线程更加简单,更加优雅。(更多的可能是在环境中就要这么用!!)
在Spring3之后,Spring引入了对多线程的支持,如果你使用的版本在3.1以前,应该还是需要通过传统的方式来实现多线程的。从Spring3同时也是新增了Java的配置方式,而且Java配置方式也逐渐成为主流的Spring的配置方式,因此后面的例子都是以Java的配置进行演示。
废话有点多,下面具体说说该如何在Spring中实现多线程,其实非常简单,只需要在配置类中添加@EnableAsync就可以使用多线程。在希望执行的并发方法中使用@Async就可以定义一个线程任务。通过spring给我们提供的ThreadPoolTaskExecutor就可以使用线程池。下面举个例子来说明
首先定义配置类
package com.hy.spring.test7; import java.util.concurrent.Executor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @ComponentScan("com.hy.spring.test7") @EnableAsync // 启用异步任务 public class ThreadConfig { // 这里是声明一个bean,类似于xml中的<bean>标签。 // Executor 就是一个线程池 @Bean public Executor getExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.initialize(); return executor; } }
定义要执行的任务
package com.hy.spring.test7; import java.util.Random; import java.util.UUID; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service // 注解的方式把AsyncService交给Spring来管理 public class AsynTaskService { // 这里可以注入spring中管理的其他bean,这也是使用spring来实现多线程的一大优势 @Async // 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行 public void f1() { System.out.println("f1 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString()); try { Thread.sleep(new Random().nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } } @Async public void f2() { System.out.println("f2 : " + Thread.currentThread().getName() + " " + UUID.randomUUID().toString()); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }
测试类
package com.hy.spring.test7; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ThreadConfig.class); AsynTaskService service = context.getBean(AsynTaskService.class); for (int i = 0; i < 10; i++) { service.f1(); // 执行异步任务 service.f2(); } context.close(); } }
输出结果
f1 : ThreadPoolTaskExecutor-5 20e6ba88-ae51-42b9-aac6-ed399419fe6d f2 : ThreadPoolTaskExecutor-2 0d7b1da4-e045-4d58-9054-e793f931cae1 f2 : ThreadPoolTaskExecutor-4 17b8d7c7-24e3-4bcf-b4da-822650a8f0be f1 : ThreadPoolTaskExecutor-3 a9b32322-1c9b-4fc7-9c2a-1f7a81f2b089 f1 : ThreadPoolTaskExecutor-1 13a85fde-73c7-4c9b-9bb2-92405d1d3ac4 f2 : ThreadPoolTaskExecutor-3 8896caaf-381c-4fc3-ab0f-a42fcc25e5fd f1 : ThreadPoolTaskExecutor-5 48246589-f8e9-4e9c-b017-8586bf14c0b0 f2 : ThreadPoolTaskExecutor-1 291b03ea-154f-46ba-bc41-69a61d1dd4d5
可以看到我们两个任务是异步进行的。
下面关于线程池的配置还有一种方式,就是直接实现AsyncConfigurer接口,重写getAsyncExecutor方法即可,代码如下:
package com.hy.spring.test7; import java.util.concurrent.Executor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @ComponentScan("com.hy.spring.test7") @EnableAsync public class ThreadConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } }