同步调用就不多说了,先来看看异步调用的好处:
使用异步调用可以不用一直等待一个方法执行完成,可以同时调用多个方法,大多数情况下对于无关联的方法
完全可以分别去执行。
Future
先从java中的Future来看吧:
我们在使用线程池的时候经常会遇到如下几个类,有着如下的关系:Runnable 实现此接口的任务线程无返回结果Callable 实现此接口的任务线程有返回结果Future 代表着未来的接口,提供了如下几个方法,具体的实现都在FutureTask中
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;RunnableFuture extends Runnable, FutureFutureTask implements RunnableFuture
调用其get方法,如果任务没有执行完成则会阻塞。这里简单理解如何实现异步调用,如果你的方法想立刻方法,
则可以立刻返回一个FutureTask,想获取方法执行的结果则调用其get方法,但是如果任务还没执行完成,调用get
方法也会阻塞。
简单使用:
public static void main(String[] args) throws ExecutionException, InterruptedException {
Future f1=exec.submit(()->{
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
return 1;
});
Future f2=exec.submit(()->{
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
return 2;
});
int result=f1.get()+f2.get();
}
具体结果和执行时间大家自己体会一下。
FutureTask
我们来看看FutureTask是怎么实现的:
首先你提交的线程任务都会被包装为FutureTask
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected RunnableFuture newTaskFor(Callable callable) {
return new FutureTask(callable);
}
线程池最终会调用其run()方法
我们先来看看其get方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
当调用get方法的时候,首先会判断任务状态是否已经完成,如果没有完成则就会进行阻塞等待。
public void run() {
...
result = c.call();
ran = true;
if (ran)
set(result);
...
}
在执行run方法的时候会调用call方法,执行完成后会唤醒执行阻塞的get().
具体阻塞和唤醒可以看源码深入了解一下。
RPC框架中的同步和异步调用
这里来看看在RPC框架Motan中是怎么实现的:
Motan中自己实现了如下几个类:
Future
Response
DefaultResponse
DefaultResponseFuture
FutureListener
首先自己定义了Future和Response接口,然后定义了ResponseFuture接口。
通过名称可以知道Future相关的都代表着未来也就是异步调用会使用。
可以看到对于同步返回和异步的返回分别有其对应的实现类:
DefaultResponse implements Response
DefaultResponseFuture implements ResponseFuture
这里的DefaultResponseFuture 类似我们上面讲到的FutureTask.
DefaultResponseFuture :
我们来看看其具体的实现
DefaultResponseFuture 中实现的 getValue() 方法类型FutureTask中的get方法,会进行阻塞直到结果返回。
它是利用protected Object lock = new Object();来实现的阻塞。
synchronized (lock){
if (!isDoing()) {
return getValueOrThrowable();//如果不是在运行就报错
}
if (timeout <= 0) {//超时时间
try {
lock.wait();
} catch (Exception e) {
...
}
//System.out.println("<= 0 getValueOrThrowable");
return getValueOrThrowable();
}
...部分代码
}
可以看到这里也是先判断任务是否还在运行,如果还在运行就会调用lock.wait();进行阻塞。
那是什么时候来lock.notifyAll()的呢?我们先看完同步调用的DefaultResponse 是如何实现的在来看
这个问题。
DefaultResponse :
我们先找到NettyClient中具体发送请求的request方法:
private Response request(Request request, boolean async) throws TransportException {
Channel channel;
Response response;
try {
// async request 异步
response = channel.request(request);
} catch (Exception e) {
}
// aysnc or sync result
response = asyncResponse(response, async);
return response;
}
在NettyChannel类中又实现了request方法:
public Response request(Request request) throws TransportException {
ResponseFuture response = new DefaultResponseFuture(request, timeout,
this.nettyClient.getUrl());
this.nettyClient.registerCallback(request.getRequestId(), response);
byte[] msg = CodecUtil.encodeObjectToBytes(this, codec, request);
ChannelFuture writeFuture = this.channel.writeAndFlush(msg);
boolean result = writeFuture.awaitUninterruptibly(timeout, TimeUnit.MILLISECONDS);
...
return response;
}
可以看到返回出去的response其实是DefaultResponseFuture.
再回到NettyClient中的方法:
private Response asyncResponse(Response response, boolean async) {
if (async || !(response instanceof ResponseFuture)) {
//心跳消息也会异步发送
return response;
}
return new DefaultResponse(response);//同步获取
}
可以看到如果是同步则会返回DefaultResponse.并且将DefaultResponseFuture包装到DefaultResponse中.
所以同步调用其实也是借助了DefaultResponseFuture的功能。
我们来看看DefaultResponse的构造函数:
public DefaultResponse(Response response) {
this.value = response.getValue();
this.exception = response.getException();
this.requestId = response.getRequestId();
this.processTime = response.getProcessTime();
this.timeout = response.getTimeout();
}
可以看到会直接调用response.getValue();也就是DefaultResponseFuture的getValue()方法。
我们上面讲了什么呢?DefaultResponseFuture的getValue()方法会怎么样呢?当然是阻塞到结果返回。
到这里我们可以看到如果是同步调用则会等到结果返回后才会返回response.而异步调用则是先返回
DefaultResponseFuture,等到想获取结果的时候则可以调用其get方法。
那我们解决最后一个问题吧,什么时候调用了lock.notifyAll(),让结果返回的。
答案是onSuccess方法。
在NettyClient中客户端获取到服务端的返回后会调用如下的handler方法:
@Override
public Object handle(Channel channel, Object message) {
Response response = (Response) message;
System.out.println("获取到message");
ResponseFuture responseFuture = NettyClient.this.removeCallback(response.getRequestId());
...
if (response.getException() != null) {
responseFuture.onFailure(response);
} else {
responseFuture.onSuccess(response);
}
return null;
}
我们发现这里会调用DefaultResponseFuture的onsuccess方法:
public void onSuccess(Response response) {
this.result = response.getValue();
this.processTime = response.getProcessTime();
done();
}
到这里就知道了,一切都在这个done方法里面了:
protected boolean done() {
synchronized (lock) {
if (!isDoing()) {//默认doing 如果不是这个状态就返回
return false;
}
//System.out.println("done");
state = FutureState.DONE;//运行完毕
//System.out.println("notifyAll");
lock.notifyAll();
}
notifyListeners();
return true;
}
终于看到我们想看到的lock.notifyAll();方法了。
总结
通过分析Motan中的实现,可以看到整体思路和futureTask很类型,因此真的有必要学好基础的思想。加油。