首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >@Async 注解的使用

@Async 注解的使用

作者头像
水货程序员
修改2018-11-20 22:11:35
5.4K0
修改2018-11-20 22:11:35
举报
文章被收录于专栏:javathingsjavathings

在 Spring 中,@Async 标注的方法,在执行的时候,是异步运行的,它运行在独立的线程中,程序不会被该方法所阻塞。

使用的时候,需要通过注解@EnableAsync 打开配置,表示可以运行异步的方法。

@Configuration  
@EnableAsync  
public class Config {
}

在异步的方法上面,标注上 @Async 注解即表示该方法是异步运行的。不过需要注意,该方法必须是 public 的,而且,在自身类中,调用异步方法是无效的。

实现异步方法的步骤如下: 第一步,配置文件中,标注可以使用异步@EnableAsync

@Configuration
@ComponentScan(value = "com.learn")
@EnableAsync
public class Config {
 
}

第二步,实现异步方法,通过@Async 注解。

返回值

实现异步方法有两种类型,一种是无返回值,一种是有返回值。

无返回值的话,和常规写法没什么不同,但是有返回值的话,需要将返回值包在 Future 对象中。Future 对象是专门存放异步响应的一个接口。

演示代码如下:

@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);
	}
}

第三步,调用异步方法。

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();
	}

观察结果:

九月 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 接口可以认为是一个函数式接口,可以用拉姆达表达式实现该接口。

演示代码如下:

@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);
 
	}

将其中一个异步方法,写一行会产生异常的代码:

@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 个线程,而且其中一个异步方法会有异常。

运行后的结果如下:

九月 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 接口处理掉了。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档