java同步变异步框架_从浅入深来看RPC框架中同步和异步调用

同步调用就不多说了,先来看看异步调用的好处:

使用异步调用可以不用一直等待一个方法执行完成,可以同时调用多个方法,大多数情况下对于无关联的方法

完全可以分别去执行。

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很类型,因此真的有必要学好基础的思想。加油。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值