实现异步API的两个常用方法

整个 Netty 的 API 都是异步的,异步处理不是一个新的机制,这个机制出来已经有一些时间了。对网络应用来说,IO 一般是性能的瓶颈,使用异步 IO 可以较大程度上高程序性能,因为异步变的越来越重要。但是它是如何工作的呢?以及有哪些不同的模式可用呢?

异步处理倡更有效的使用资源,它允许你创建一个任务,当有事件发生时将获得通知并等待事件完成。这样就不会阻塞,不管事件完成与否都会及时返回,资源利用率更高,程序可以利用剩余的资源做一些其他的事情。

本节将说明一起工作或实现异步 API 的两个最常用的方法,并讨论这些技术之间的差异

一、Callback(回调)

回调是异步处理的一种技术。一个回调是被传递到并且执行完该方法。
下面代码是一个简单的回调:

public class Worker {    

  public void doWork() {        
    Fetcher fetcher = new MyFetcher(new Data(1, 0));  
    fetcher.fetchData(new FetcherCallback() {     
       
        @Override           
        public void onData(Data data) throws Exception {     
                  System.out.println("Data received : " + data);            
        }            
        @Override            
        public void onError(Throwable cause) {                
            System.out.println("An err occured : " + cause.getMessage());            }        
        });    
   }    

   public static void main(String[] args) {        
        Worker w = new Worker();        
        w.doWork();    
   }
}
public interface Fetcher {    
    void fetchData(FetcherCallback callback);
}
public class MyFetcher implements Fetcher {    
    final Data data;    

    public MyFetcher(Data data) {        
        this.data = data;    
    }    

    @Override    
    public void fetchData(FetcherCallback callback) {        
        try {           
             callback.onData(data);        
        } catch (Exception e) {            
            callback.onError(e);        
        }    
    }
}
public interface FetcherCallback {    

    void onData(Data data) throws Exception;    
    void onError(Throwable cause);
}
public class Data {   
 
    private int m;    
    private int n;    

    public Data(int m, int n) {        
        this.m = m;        
        this.n = n;    
    }    

    @Override    
    public String toString() {        
        int r = n / m;        
        return n + "/" + m + " = " + r;    
    }
}

上面的例子只是一个简单的模拟回调,要明白其所表达的含义。Fetcher.fetchData()方法需传递一个FetcherCallback 类型的参数,当获得数据或发生错误时被回调。对于每种情况都供了同意的方法:

FetcherCallback.onData(),将接收数据时被调用

FetcherCallback.onError(),发生错误时被调用

因为可以将这些方法的执行从"caller"线程移动到其他的线程执行;但也不会保证 FetcherCallback 的每个方法都会被执行。回调过程有个问题就是当你使用链式调用很多不同的方法会导致线性代码;有些人认为这种链式调用方法会导致代码难以阅读,但是我认为这是一种风格和习惯问题。例如,基于 Javascript 的 Node.js 越来越受欢迎,它使用了大量的回调,许多人都认为它的这种方式利于阅读和编写。

二、Futures

第二种技术是使用 Futures。Futures 是一个抽象的概念,它表示一个值,该值可能在某一点变得可用。一个 Future 要么获得计算完的结果,要么获得计算失败后的异常。Java 在 java.util.concurrent 包中附带了 Future 接口,它使用 Executor 异步执行。例如下面的代码,每传递一个 Runnable 对象到 ExecutorService.submit()方法就会得到一个回调的 Future,你能使用它检测是否执行完成。

public class FutureExample {    
    public static void main(String[] args) {        
        ExecutorService executor = Executors.newCachedThreadPool();        
        Runnable task1 = new Runnable() {            

            @Override            
            public void run() {                
                // do something                
                System.out.println("i am task1...");            
            }        
        };        

        Callable task2 = new Callable() {            

            @Override            
            public Integer call() throws Exception {                
               // do something                
               return new Integer(100);            
            }        
        };        

        Future f1 = executor.submit(task1);           
        Future f2 = executor.submit(task2);    

        System.out.println("task1 is done ? " + f1.isDone());   
        System.out.println("task2 is done ? " + f2.isDone());        

        // waiting task1 done        
        while (f1.isDone()) {            
            System.out.println("task1 done");            
            break;        
        }        
        // waiting task2 done        
        while (f2.isDone()) {            
            System.out.println("task2 done");            
            break;        
        }    
    }
}

有时候使用 Future 感觉很丑陋,因为你需要间隔检查 Future 是否已完成,而使用回调会直接收到返回通知。看完这两个常用的异步执行技术后,你可能想知道使用哪个最好?这里没有明确的答案。事实上,Netty 两者都使用,供两全其美的方案。

你可能感兴趣的:(实现异步API的两个常用方法)