一、使用方式
实现异步方法比较简单,只需下边两步
- 在启动类或者配置类上添加
@EnableAsync
注解; - 在方法上加
@Async
注解。
示例代码
@Service
@EnableAsync
public class TestAsyncServiceImpl {
/**
* 没有返回值的异步方法
*/
@Async
public void testAsync1() {
// do something
}
/**
* 有返回值的异步方法
* @return
*/
@Async
public Future<String> testAsync2(){
// do something
Future<String> future = new AsyncResult<String>("success");
return future;
}
}
注意事项
- 同一个类中的同步方法调用本类中的
@Async
异步方法,异步方法不生效; @Async
异步方法的线程池,默认使用SimpleAsyncTaskExecutor
,这个不是真的线程池,它不会重复使用线程,每次调用都会创建一个新的线程,并发大的时候会产生严重的性能问题。
二、自定义线程池
@Async
异步方法的默认线程池是有风险问题的,我们可以指定自定义线程池。
下边给出两种自定义线程池的方式,我们在用的时候二选一即可。
-
@Bean
注解方式@Configuration public class TestAsyncThreadPool { @Bean("testAsyncExecutor") public Executor testExecutor(){ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心池大小:线程池维护线程的最少数量 executor.setCorePoolSize(5); //最大线程数 executor.setMaxPoolSize(100); //队列大小 executor.setQueueCapacity(1000); //线程空闲时间 executor.setKeepAliveSeconds(60); //线程名称前缀 executor.setThreadNamePrefix("test-asyn-"); //拒绝策略:当最大线程数已经达到maxPoolSize的时候,如何处理新任务 // ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 // ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 // ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,执行后面的任务 // ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); //线程池注册优雅停机:当要停止应用时,等待所有线程执行完再停止 executor.setWaitForTasksToCompleteOnShutdown(true); //设置等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。 executor.setAwaitTerminationSeconds(120); return executor; } }
-
传统xml方式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="testAsyncExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 线程池维护线程的最少数量 --> <property name="corePoolSize" value="5" /> <!-- 线程池维护线程的最大数量 --> <property name="maxPoolSize" value="100" /> <!-- 允许的空闲时间 --> <property name="keepAliveSeconds" value="1000" /> <!-- 缓存队列 --> <property name="queueCapacity" value="60" /> <!-- 线程名称前缀 --> <property name="threadNamePrefix" value="test-asyn-" /> <!-- 对拒绝task的处理策略 --> <property name="rejectedExecutionHandler"> <bean class="java.util.concurrent.ThreadPoolExecutor$AbortPolicy" /> </property> <!-- 线程池注册优雅停机:当要停止应用时,等待所有线程执行完再停止 --> <property name="waitForTasksToCompleteOnShutdown" value="true"/> <!-- 设置等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住 --> <property name="awaitTerminationSeconds" value="120"/> </bean> </beans>
xml文件可以通过
dispatcherservlet
加载,也可以通过配置类引入:@Configuration @ImportResource(value = { "classpath:spring/spring-threadpool.xml" }) public class TestAsyncThreadPool { }
三、自定义线程池的使用
使用方式比较简单,直接将之前的
@Async
改为@Async("testAsyncExecutor")
@Service
@EnableAsync
public class TestAsyncServiceImpl {
/**
* 没有返回值的异步方法
*/
@Async("testAsyncExecutor")
public void testAsync1() {
// do something
}
/**
* 有返回值的异步方法
* @return
*/
@Async("testAsyncExecutor")
public Future<String> testAsync2(){
// do something
Future<String> future = new AsyncResult<String>("success");
return future;
}
}
四、线程池execute(Runable)方法执行过程
处理优先级:corePool > queue > maxPool > rejectedExecutionHandler
- 如果线程池中的线程数 < corePoolSize,即使有空闲线程,新请求也会新建线程处理;
- 如果线程池中的线程数 = corePoolSize,有空闲线程,新请求会使用空闲的线程;
- 如果线程池中的线程数 = corePoolSize,没有空闲线程,队列中数据 < queueCapacity,新请求会被放到队列中;
- 如果队列中数据 = queueCapacity,新请求会继续新建线程处理;
- 如果线程池中的线程数 = maxPoolSize,新请求会通过配置的拒绝策略处理。