从0到1学系统通信——异步通信的常用技术(二)

网易视频云是网易公司旗下的视频云服务产品,以Paas服务模式,向开发者提供音视频编解码SDK和开放API,助力APP接入音视频功能。现在,网易视频云的技术专家给大家分享一篇技术性文章:从0到1学系统通信——异步通信的常用技术(二)。

上一篇文章介绍了java NIO,异步处理提倡更有效的使用资源,它允许你创建一个任务,当有事件发生时将获得通知并等待事件完成。这样就不会阻塞,不管事件完成与否都会及时返回,资源利用率更高,程序可以利用剩余的资源做一些其他的事情。本章将介绍异步调用中最常用的两种方法。

CallBacks(回调)

接触过GUI编程的都熟悉这种方法,当你在界面按下一个按钮,会调用回调函数进行处理。回调的思想在GUI的场景下,使得编程变得简单和易于理解。当然回调这种方法使用的地方很多,在了解其原理后,以后我们在看到这种方法的时候,就能加可以理解它是如何运行的了。下面是一个简单的回调代码示例。

回调函数接口:

public interface CallBack { void rightProcess(Request input) throws Exception; void errorProcess(Throwable cause);

}

示例类:

public class Activer { private Listlisteners = new ArrayList();

public void addListener(Request request) {

Listener listener = new Listener(request);

listener.registerListener(new CallBack() {

@Override

public void rightProcess(Request input) throws Exception {

System.out.println("use the right method to process " + input);

}

@Override

public void errorProcess(Throwable cause) {

System.out.println("use the error method to process " + cause.getMessage());

}

});

listeners.add(listener);

}

public void activeListen() { for (Listener listener : listeners) {

listener.listen();

}

}

public static void main(String avg[]) {

Activer activer = new Activer();

activer.addListener(new Request("1", "first to input"));//一些应用场景中,可以一个线程注册回调函数

activer.activeListen();//另一个线程触发回调函数

}

}

请求类:

public class Request { final String id; final String message;

public Request(String id, String message) { this.id = id; this.message = message;

}

public String toString() { return id + ":" + message;

}

}监听类:

public class Listener { final Request input; private CallBack callback; public Listener(Request input) { this.input = input;

}

public void registerListener(CallBack callback) { this.callback = callback;

}

public void listen() { try {

callback.rightProcess(input);

} catch (Exception e) {

callback.errorProcess(e);

}

}

} 在以上代码示例中解决两个问题:

1.注册回调函数和实际调用回调函数时间点的解耦

2.触发回调函数的线程可以和实际执行回调函数的线程解耦

有了以上两个特性,再回想之前接触的GUI编程中的注册动作的回调函数,其原理一目了然了。

在上一章提到的Reactor模式( Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。)也是采用了这种实现方式。可见回调方法在异步处理中,具有广泛应用,掌握其核心原理和思路,对于理解其他异步通信框架有很大帮助。

Futures

在没有接触JAVA之前,看到这个future确实有很多困惑。这是一种全新的概念。Futures是一个抽象的概念,它表示一个值,该值可能在某一点变得可用。一个Future要么获得计算完的结果,要么获得计算失败后的异常。Java在java.util.concurrent包中附带了Future接口,它使用Executor异步执行。例如下面的代码,每传递一个Runnable对象到ExecutorService.submit()方法就会得到一个回调的Future,你能使用它检测是否执行完成。

public class FutureExample {

public static void main(String[] args) throws Exception {

ExecutorService executor = Executors.newCachedThreadPool();

Runnable task1 = new Runnable() { @Override

public void run() { //do something

System.out.println("i am task1.....");

}

};

Callabletask2 = new Callable() { @Override

public Integer call() throws Exception { //do something

return new Integer(100);

}

};

Future f1 = executor.submit(task1);

Futuref2 = executor.submit(task2);

System.out.println("task1 is completed? " + f1.isDone());

System.out.println("task2 is completed? " + f2.isDone()); //waiting task1 completed

while(f1.isDone()){

System.out.println("task1 completed."); break;

} //waiting task2 completed

while(f2.isDone()){

System.out.println("return value by task2: " + f2.get()); break;

}

}

}查看源码发现,当执行Futuref2 = executor.submit(task2)时,调用的接口为:AbstractExecutorService类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);

}FutureTask类public FutureTask(Callable<V> callable) {        if (callable == null)            throw new NullPointerException();        this.callable = callable;        this.state = NEW;       // ensure visibility of callable}也就是返回的Future是一个futureTask。而public class FutureTask<V> implements RunnableFuture<V>;public interface RunnableFuture<V> extends Runnable, Future<V> 。因此返回的future就是一个含有执行过程中状态state的Runnable对象。由于这个Runnable对象的执行情况未知,但是其状态可以通过对state的读取感知到,其结果可以通过FutureTask的属性outcome得到。FutureTask类中的属性和run()方法如下:private volatile int state;/** The underlying callable; nulled out after running */private Callable<V> callable;/** The result to return or exception to throw from get() */private Object outcome; // non-volatile, protected by state reads/writes/** The thread running the callable; CASed during run() */private volatile Thread runner;/** Treiber stack of waiting threads */private volatile WaitNode waiters;public void run() {        if (state != NEW ||

!UNSAFE.compareAndSwapObject(this, runnerOffset,                                         null, Thread.currentThread()))            return;        try {

Callablec = callable;            if (c != null && state == NEW) {

V result;                boolean ran;                try {

result = c.call();

ran = true;

} catch (Throwable ex) {

result = null;

ran = false;

setException(ex);

}                if (ran)

set(result);

}

} finally {            // runner must be non-null until state is settled to

// prevent concurrent calls to run()

runner = null;            // state must be re-read after nulling runner to prevent

// leaked interrupts

int s = state;            if (s >= INTERRUPTING)

handlePossibleCancellationInterrupt(s);

}}在JAVA中Future (interface) 的使用是非常简单的,其实Future 本身是一种被广泛运用的并发设计模式,可在很大程度上简化需要数据流同步的并发应用开发。前面讲解java中Future的实现,下面来说明Future设计模式。Future模式中有如下几个概念:

1 Client参与者,发送请求方,调用子线程的,它一旦发送请求后,就获取一个VirtualData,作为请求的结果。对应于FutureExample示例中调用executor.submit()

2 Host参与者,接受请求者,会为请求者返回一个FutureData对象。并且会启动一个线程来执行真正的任务,最后将任务的返回结果RealData对象赋值给FutureData对象。 对应于javaFuture实现中的AbstractExecutorService类。

3 VirturalData参与者,用来统一代表FutureData参与者与RealData参与者,是他们的统一接口。对应于Future接口。

4 FutureData参与者,它是当做工作还没有正式完成前的临时代表,会在其中进行线程的等待,唤醒,以及提供Client参与者调用处理结果的方法。对应于FutureTask中的outcome

5 RealData参与者,具体真正进行操作数据的类。对应于FutureExample示例中传入的task2

在熟练掌握了以上两种异步调用方法之后,下一章,我们将展开对netty4.0的学习。

你可能感兴趣的:(云计算,通信技术,视频云,网易视频云)