同步调用就不多说了,先来看看异步调用的好处: 使用异步调用可以不用一直等待一个方法执行完成,可以同时调用多个方法,大多数情况下对于无关联的方法 完全可以分别去执行。
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, Future<V>
FutureTask implements RunnableFuture<V> 调用其get方法,如果任务没有执行完成则会阻塞。这里简单理解如何实现异步调用,如果你的方法想立刻方法, 则可以立刻返回一个FutureTask,想获取方法执行的结果则调用其get方法,但是如果任务还没执行完成,调用get 方法也会阻塞。 简单使用: public static void main(String[] args) throws ExecutionException, InterruptedException { Future<Integer> f1=exec.submit(()->{ LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); return 1; }); Future<Integer> f2=exec.submit(()->{ LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); return 2; }); int result=f1.get()+f2.get(); } 具体结果和执行时间大家自己体会一下。
FutureTask
我们来看看FutureTask是怎么实现的: 首先你提交的线程任务都会被包装为FutureTask public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; } protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(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很类型,因此真的有必要学好基础的思想。加油。