原文来自:https://www.cnblogs.com/memoryXudy/p/7737418.html
引用:在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法。
1. 何为异步调用?
在解释异步调用之前,我们先来看同步调用的定义;同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。
例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法;如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕; 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。
2. 常规的异步调用处理方式
在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。
3. @Async介绍
在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。
一、不带参数的异步调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@Override
@Async
public
String asyncMethodWithVoidReturnType() {
System.out.println(
"线程名称:"
+Thread.currentThread().getName() +
" be ready to read data!"
);
try
{
Thread.sleep(
1000
*
5
);
System.out.println(Thread.currentThread().getName()+
"---------------------》》》无返回值延迟5秒:"
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
// for (int i =0 ;i<100000000;i++){
// System.out.println("Execute method asynchronously.-----------------------" +
// "--"+ Thread.currentThread().getName()+i);
// }
return
"已进入到异步"
;
}
|
二、带参数的异步调用 异步方法可以传入参数
1
2
3
4
5
|
@Async
public
void
asyncInvokeWithParameter(String s) {
log.info(
"asyncInvokeWithParameter, parementer={}"
, s);
throw
new
IllegalArgumentException(s);
}
|
三、调用返回Future 的异步线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Async
public
Future<String> asyncInvokeReturnFuture(
int
i) {
log.info(
"asyncInvokeReturnFuture, parementer={}"
, i);
Future<String> future;
try
{
Thread.sleep(
1000
*
4
);
future =
new
AsyncResult<String>(
"success:"
+ i);
}
catch
(InterruptedException e) {
future =
new
AsyncResult<String>(
"error"
);
}
return
future;
}
|
以上调用返回Future 的异步线程,返回的数据类型为Future类型,其为一个接口。具体的结果类型为AsyncResult。
调用返回结果的异步方法示例:
1
2
3
4
|
// 多线程Future
Future<String> future = mainService.asyncInvokeReturnFuture(
100
);
//future 异常捕捉
System.out.println(
"Future延迟3秒:"
+future.get());
|
结果:这些获取异步方法的结果信息,是通过不停的检查Future的状态来获取当前的异步方法是否执行完毕来实现的。
四 、基于@Async调用中的异常处理机制
在异步方法中,如果出现异常,对于调用者caller而言,是无法感知的。如果确实需要进行异常处理,则按照如下方法来进行处理:
1. 自定义实现AsyncTaskExecutor的任务执行器
在这里定义处理具体异常的逻辑和方式。
2. 配置由自定义的TaskExecutor替代内置的任务执行器
示例步骤1,自定义的TaskExecutor
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public
class
ExceptionHandlingAsyncTaskExecutor
implements
AsyncTaskExecutor {
private
AsyncTaskExecutor executor;
public
ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) {
this
.executor = executor;
}
用独立的线程来包装,@Async其本质就是如此
public
void
execute(Runnable task) {
executor.execute(createWrappedRunnable(task));
}
public
void
execute(Runnable task,
long
startTimeout) {
/用独立的线程来包装,
@Async
其本质就是如此
executor.execute(createWrappedRunnable(task), startTimeout);
}
public
Future submit(Runnable task) {
return
executor.submit(createWrappedRunnable(task));
//用独立的线程来包装,@Async其本质就是如此。
}
public
Future submit(
final
Callable task) {
//用独立的线程来包装,@Async其本质就是如此。
return
executor.submit(createCallable(task));
}
private
Callable createCallable(
final
Callable task) {
return
new
Callable() {
public
T call()
throws
Exception {
try
{
return
task.call();
}
catch
(Exception ex) {
handle(ex);
throw
ex;
}
}
};
}
private
Runnable createWrappedRunnable(
final
Runnable task) {
return
new
Runnable() {
public
void
run() {
try
{
task.run();
}
catch
(Exception ex) {
handle(ex);
}
}
};
}
private
void
handle(Exception ex) {
//具体的异常逻辑处理的地方
System.err.println(
"Error during @Async execution: "
+ ex);
}
}
|
分析: 可以发现其是实现了AsyncTaskExecutor, 用独立的线程来执行具体的每个方法操作。在createCallable和createWrapperRunnable中,定义了异常的处理方式和机制。
handle()就是未来我们需要关注的异常处理的地方。。
配置文件中的内容:
1
2
3
4
5
6
|
<task:annotation-driven executor=
"exceptionHandlingTaskExecutor"
scheduler=
"defaultTaskScheduler"
/>
<bean id=
"exceptionHandlingTaskExecutor"
class
=
"nl.jborsje.blog.examples.ExceptionHandlingAsyncTaskExecutor"
>
<constructor-arg ref=
"defaultTaskExecutor"
/>
</bean>
<task:executor id=
"defaultTaskExecutor"
pool-size=
"5"
/>
<task:scheduler id=
"defaultTaskScheduler"
pool-size=
"1"
/>
|
分析: 这里的配置使用自定义的taskExecutor来替代缺省的TaskExecutor。
7. @Async调用中的事务处理机制
在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。
那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional.
例如: 方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。
方法B,使用了@Async来标注, B中调用了C、D,C/D分别使用@Transactional做了标注,则可实现事务控制的目的。
8. 总结
通过以上的描述,应该对@Async使用的方法和注意事项了。