spring中异步方法的配置
1、把方法放在一个单独的service类中,用@Async标注方法,用@EnableAsync标注类。也可以用@Async标注service类。
这个时候,这个方法就是异步方法了,调用时会异步执行。但是,每执行一次这个方法都要生成一个线程(线程名以"SimpleAsyncTaskExecutor-"开头),执行完之后线程再销毁。这样很不好,最好有一个线程池,每次方法执行的时候都从线程池中取线程,执行完之后再放回到线程池中。这就有了第2步。
2、在applicationContext.xml中配置一个ExecutorService实例。方法有很多种,以下只是其中一种:
<bean id="asyncThreadPoolExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="20"/> <property name="maxPoolSize" value="40"/> <property name="keepAliveSeconds" value="0"/> <property name="queueCapacity" value="3000"/> <property name="threadNamePrefix" value="asyncThreadPool-"/> </bean>
如果不用上述方法显式地创建线程池,则也可以在applicationContext.xml文件中配置
<task:executor id="annotationExecutor" pool-size="20-40" queue-capacity="3000" keep-alive="0" rejection-policy="ABORT"/>
这样就会产生一个ThreadPoolTaskExecutor实例。不过这种方式生成的线程池里的线程的名字不能像第一种方法那样自定义,而是固定以"annotationExecutor-"为开头的。
如果配置了多个<task:executor/>,则需要在配置@Async注解的时候,配置其value属性值为某个executorId,这样执行方法时就会使用这个指定的线程池,如@Async(value = "xxx"),否则不会用任何一个线程池,而是每执行一次就创建一个线程。
以上配置针对没有返回值的方法没问题,但是如果异步方法有返回值,而且想要获取该方法的返回值,那么这个异步方法就不能简简单单的返回原来想返回的值类型了,得返回xxxFuture<T>类型。阿里规约插件提醒如下:@Async methods should return void, java.util.concurrent.Future, org.springframework.util.concurrent.ListenableFuture or java.util.concurrent.CompletableFuture。
示例如下:
@Async(value = "annotationExecutor") public ListenableFuture<String> print() { try { Thread.sleep(3000); LOGGER.info("async thread: " + Thread.currentThread().getName()); } catch (Exception e) { LOGGER.error(e); } return new AsyncResult<String>("异步返回值"); }
AsyncResult全类名是org.springframework.scheduling.annotation.AsyncResult,实现了ListenableFuture接口。
需要特别提醒下, <task:annotation-driven/>这个跟异步没有半毛钱关系。跟定时任务有点关系,配置了的话,就不用@EnableScheduling注解了。
spring中定时任务的配置
1、用@Scheduled标注方法,用@EnableScheduling标注类,就可以了。
这时现在每执行一次定时任务,就会创建一个线程,执行完后销毁。需要引入线程池。这就需要第2步
2、在applicationContext.xml中配置如下:
<bean id="scheduledThreadPoolExecutor" class="java.util.concurrent.ScheduledThreadPoolExecutor"> <constructor-arg name="corePoolSize" value="10"/> <constructor-arg name="threadFactory" ref="threadFactory"/> </bean>
并新建一个Config类(类名随便),内容如下:
@Configuration public class Config { @Bean("threadFactory") public ThreadFactory getThreadFactory() { return new ThreadFactoryBuilder().setNameFormat("scheduled-pool-%d").setPriority(10).setDaemon(false).build(); } }
如果不用上述方法显式创建定时任务线程池,则还可以在applicationContext.xml中配置:
<task:scheduler id="scheduler" pool-size="5"/>
这种方法会生成一个指定大小的org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler实例。不过同样不能自定义线程名,线程名固定以"scheduler-"开头。
实际测试发现,如果既显式配置ScheduledExecutorService实例,又配置<task:scheduler/>的话,定时任务会用<task:scheduler/>对应的线程池。显示配置的ScheduledExecutorService实例没用。
最佳实践:
import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.google.common.util.concurrent.ThreadFactoryBuilder; @Configuration public class Config { @Bean("asyncThreadPoolExecutor") public ExecutorService getThreadPoolExecutor() { return new ThreadPoolExecutor(20, 40, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(3000), new ThreadFactoryBuilder().setNameFormat("asyncThreadPool-%d").setPriority(10).setDaemon(false).build(), new ThreadPoolExecutor.AbortPolicy()); } @Bean("scheduledThreadPoolExecutor") public ScheduledExecutorService getThreadFactory() { return new ScheduledThreadPoolExecutor(10, new ThreadFactoryBuilder().setNameFormat("scheduledThreadPool-%d").setPriority(10).setDaemon(false).build(), new ThreadPoolExecutor.AbortPolicy()); } }