串行调用:在某个调用中,需要顺序调用 A, B, C三个过程方法,且是同步调用,则需要将三个方法都顺序执行完毕之后,整个调用过程执行完毕
异步调用:上述场景中,若B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了,不需要等待B方法的处理结果
示例:
import
com.google.common.eventbus.AllowConcurrentEvents;
import
com.google.common.eventbus.AsyncEventBus;
import
com.google.common.eventbus.Subscribe;
import
java.util.concurrent.Executors;
/**
* Created by zhuqiuhui on 2017/9/13.
*/
public
class
AsyncTest {
public
static
void
main(String[] args)
throws
InterruptedException {
AsyncEventBus asyncEventBus =
new
AsyncEventBus(Executors.newFixedThreadPool(
3
));
asyncEventBus.register(
new
Object(){
@AllowConcurrentEvents
@Subscribe
public
void
handleStringEvent(String s)
throws
InterruptedException {
Thread.sleep(
3000
);
System.out.println(s);
// int test = 4/0;
throw
new
NullPointerException();
}
@AllowConcurrentEvents
@Subscribe
public
void
handleIntegerEvent(Integer i)
throws
InterruptedException {
Thread.sleep(
1000
);
System.out.println(i);
throw
new
IllegalStateException();
}
});
asyncEventBus.post(
"这是个测试"
);
asyncEventBus.post(
4
);
System.out.println(
"继续主函数......"
);
Thread.sleep(
5000
);
System.out.println(
"主函数结束......"
);
}
}
|
输出:
继续主函数......
4
九月
13
,
2017
5
:
05
:
42
下午 com.google.common.eventbus.EventBus$LoggingSubscriberExceptionHandler handleException
严重: Could not dispatch event: service.AsyncTest$
1
@6fb554cc
to
public
void
service.AsyncTest$
1
.handleIntegerEvent(java.lang.Integer)
throws
java.lang.InterruptedException
九月
13
,
2017
5
:
05
:
44
下午 com.google.common.eventbus.EventBus$LoggingSubscriberExceptionHandler handleException
这是个测试
严重: Could not dispatch event: service.AsyncTest$
1
@6fb554cc
to
public
void
service.AsyncTest$
1
.handleStringEvent(java.lang.String)
throws
java.lang.InterruptedException
主函数结束......
|
由上可以看出,已经注册的方法的状态不影响主流程,适合异步场景。
注意:Guava EventBus中默认订阅方法为线程不安全的,在异步调度时会自动将其包装成线程安全的方法。对于一般线程安全的实现上,可以通过@AllowConcurrentEvents注解来标识。
|
import
org.springframework.scheduling.annotation.Async;
import
org.springframework.scheduling.annotation.AsyncResult;
import
org.springframework.stereotype.Component;
import
java.util.concurrent.Future;
/**
* Created by zhuqiuhui on 2017/9/14.
*/
@Component
public
class
AsynServiceProxy {
@Async
public
Future
try
{
Thread.sleep(
7000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(s);
return
new
AsyncResult<>(
"这是返回结果"
);
}
}
|
异步方法必须单独写一个类中
@Service
public
class
PromotionServiceImpl
implements
IPromotionService {
@Resource
private
AsynServiceProxy asynServiceProxy;
@Override
public
void
testAsyncMethod(){
Future
"test"
);
System.out.println(
"继续主函数......"
);
System.out.println(
"主函数结束......"
);
try
{
System.out.println(s.get() +
"---------"
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
catch
(ExecutionException e) {
e.printStackTrace();
}
}
}
|
输出结果:
继续主函数......
主函数结束......
test
这是返回结果---------
|
s.get() 等待线程的结束,取得返回值。
如service类中加:
@Service
public
class
PromotionServiceImpl
implements
IPromotionService {
@Resource
private
IPromotionRuleService iPromotionRuleService;
@Override
@Async
public
void
testAsync(String s) {
try
{
Thread.sleep(
7000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(s);
}
}
|
原因:准备注入的 bean,已经是代理 bean,而不是原始 bean。
解决办法:单独写一个代理 bean,存放异步方法,并且代理 bean 中,不再依赖注入自身 bean。
注意:在同一个类中,一个方法调用另外一个有@Async注解的方法,注解是不会生效的。因为spring 在扫描包的时候,如果发现类或类中有方法有@Asycn注解,spring会为这个bean动态地生成一个代理类。当方法被调用时,实际是调用代理类的。然而,如果这个有@Async的方法是被同一个类中的其他方法调用的,那么该方法的调用就没有通过代理类,而是直接通过原来的那个bean,所以就不会启动新线程异步调用。