@Async 注解的使用
4,193 total views, 3 views today
在 Spring 中,@Async 标注的方法,在执行的时候,是异步运行的,它运行在独立的线程中,程序不会被该方法所阻塞。
使用的时候,需要通过注解@EnableAsync 打开配置,表示可以运行异步的方法。
1 2 3 4 |
@Configuration @EnableAsync public class Config { } |
在异步的方法上面,标注上 @Async 注解即表示该方法是异步运行的。不过需要注意,该方法必须是 public 的,而且,在自身类中,调用异步方法是无效的。
实现异步方法的步骤如下:
第一步,配置文件中,标注可以使用异步@EnableAsync
1 2 3 4 5 6 |
@Configuration @ComponentScan(value = "com.learn") @EnableAsync public class Config { } |
第二步,实现异步方法,通过@Async 注解。
返回值
实现异步方法有两种类型,一种是无返回值,一种是有返回值。
无返回值的话,和常规写法没什么不同,但是有返回值的话,需要将返回值包在 Future 对象中。Future 对象是专门存放异步响应的一个接口。
演示代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@Component public class AsyncDemo { @Async public void asyncThing() { System.out.println("calling asyncThing," + Thread.currentThread().getName() + "," + new Date()); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } int i=100/0; System.out.println("asyncThing Finished," + Thread.currentThread().getName() + "," + new Date()); } @Async public Future<Integer> asyncSquare(int x) { System.out.println("calling asyncSquare," + Thread.currentThread().getName() + "," + new Date()); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("asyncSquare Finished," + Thread.currentThread().getName() + "," + new Date()); return new AsyncResult<Integer>(x*x); } } |
第三步,调用异步方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); AsyncDemo bean = applicationContext.getBean(AsyncDemo.class); System.out.println(new Date()); bean.asyncThing(); Future<Integer> future_int=bean.asyncSquare(3); System.out.println(new Date()); try { System.out.println(future_int.get()); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } //防止主进程立即运行完毕,延迟10秒退出主进程 try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } applicationContext.close(); } |
观察结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
九月 15, 2018 9:51:53 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@533ddba: startup date [Sat Sep 15 21:51:53 CST 2018]; root of context hierarchy 九月 15, 2018 9:51:54 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization 信息: Bean 'config' of type [com.learn.Config$$EnhancerBySpringCGLIB$$31aaeebc] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 九月 15, 2018 9:51:54 下午 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor initialize 信息: Initializing ExecutorService Sat Sep 15 21:51:54 CST 2018 Sat Sep 15 21:51:54 CST 2018 calling asyncThing,MyAsync-1,Sat Sep 15 21:51:54 CST 2018 asyncThing Finished,MyAsync-1,Sat Sep 15 21:51:56 CST 2018 calling asyncSquare,MyAsync-1,Sat Sep 15 21:51:56 CST 2018 asyncSquare Finished,MyAsync-1,Sat Sep 15 21:51:58 CST 2018 9 |
可以看到,异步方法是立即返回结果值,不会阻塞主线程的。默认的,打开异步开关后,Spring 会使用一个 SimpleAsyncTaskExecutor 作为线程池,该线程默认的并发数是不受限制的。所以每次异步方法来,都会获取一个新线程去运行它。
AsyncConfigurer 接口
Spring 4 中,对异步方法可以做一些配置,将配置类实现 AsyncConfigurer 接口后,可以实现自定义线程池的功能,和统一处理异步方法的异常。
如果不限制并发数,可能会造成系统压力。AsyncConfigurer 接口中的方法 Executor getAsyncExecutor() 实现自定义线程池。控制并发数。
AsyncConfigurer 接口中的方法 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() 用于处理异步方法的异常。
AsyncUncaughtExceptionHandler 接口,只有一个方法:
void handleUncaughtException(Throwable ex, Method method, Object… params);
因此,AsyncUncaughtExceptionHandler 接口可以认为是一个函数式接口,可以用拉姆达表达式实现该接口。
演示代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@Configuration @ComponentScan(value = "com.learn") @EnableAsync public class Config implements AsyncConfigurer { //自定义线程池,控制并发数,将线程池的大小设置成只有1个线程。 @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor(); threadPool.setCorePoolSize(1); threadPool.setMaxPoolSize(1); threadPool.setWaitForTasksToCompleteOnShutdown(true); threadPool.setAwaitTerminationSeconds(60 * 15); threadPool.setThreadNamePrefix("MyAsync-"); threadPool.initialize(); return threadPool; } //统一处理异常 @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (throwable, method, objects) -> System.out.println( "-- exception handler -- " + throwable + "-- method -- " + method + "-- objects -- " + objects); } |
将其中一个异步方法,写一行会产生异常的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Async public void asyncThing() { System.out.println("calling asyncThing," + Thread.currentThread().getName() + "," + new Date()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } int i=100/0;//抛出异常 System.out.println("asyncThing Finished," + Thread.currentThread().getName() + "," + new Date()); } |
如上代码,演示中,将线程池的大小设置成只有 1 个线程,而且其中一个异步方法会有异常。
运行后的结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
九月 15, 2018 9:52:47 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@533ddba: startup date [Sat Sep 15 21:52:47 CST 2018]; root of context hierarchy 九月 15, 2018 9:52:48 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization 信息: Bean 'config' of type [com.learn.Config$$EnhancerBySpringCGLIB$$31aaeebc] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 九月 15, 2018 9:52:48 下午 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor initialize 信息: Initializing ExecutorService Sat Sep 15 21:52:48 CST 2018 Sat Sep 15 21:52:48 CST 2018 calling asyncThing,MyAsync-1,Sat Sep 15 21:52:48 CST 2018 -- exception handler -- java.lang.ArithmeticException: / by zero-- method -- public void com.learn.entity.AsyncDemo.asyncThing()-- objects -- [Ljava.lang.Object;@1626ab0b calling asyncSquare,MyAsync-1,Sat Sep 15 21:52:50 CST 2018 asyncSquare Finished,MyAsync-1,Sat Sep 15 21:52:52 CST 2018 9 |
可以看到,由于线程池中只有 1 个线程,所以两个异步方法,使用的是同一个线程运行的。异常的处理也由 AsyncUncaughtExceptionHandler 接口处理掉了。
原创文章,转载请注明出处!http://www.javathings.top/async注解的使用/