總結!!!總結!!!java回調以及future模式

老是忘記,我這裏直接寫實際的東西,看其餘的博客都是類圖,文字描述,這裏直接用代碼描述.java

疑問:什麼是回調

回調,回調。要先有調用,纔有調用者和被調用者之間的回調。因此在百度百科中是這樣的:git

軟件模塊之間老是存在着必定的接口,從調用方式上,能夠把他們分爲三類:同步調用、回調和異步調用微信

回調是一種特殊的調用,至於三種方式也有點不一樣。異步

一、同步回調,即阻塞,單向ide

二、回調,即雙向(相似自行車的兩個齒輪)。函數

三、異步調用,即經過異步消息進行通知。測試

1.同步調用

直接上代碼 b方法調用a方法this

package java.lang;
/**
 * @author: zhangzeli
 * @date 16:02 2018/4/20
 * <P></P>
 */
public class Test {
    public String a(){
        System.out.println("i am a");
        return "a";
    }
    public String b(){
        a();
        System.out.println("i am b");
        return "b";
    }
}

二、回調,即雙向(相似自行車的兩個齒輪)。

接下來看一下回調的代碼示例,代碼模擬的是這樣一種場景:老師問學生問題,學生思考完畢回答老師。 首先定義一個回調接口,只有一個方法tellAnswer(int answer),即學生思考完畢告訴老師答:spa

/**
 * 回調接口
 */
public interface Callback {
    public void tellAnswer(int answer);
}

定義一個老師對象,實現Callback接口:線程

/**
 * 老師對象
 */
public class Teacher implements Callback {
    private Student student;
    
    public Teacher(Student student) {
        this.student = student;
    }
    public void askQuestion() {
        student.resolveQuestion(this);
    }
    @Override
    public void tellAnswer(int answer) {
        System.out.println("知道了,你的答案是" + answer);
    }
    
}

老師對象有兩個public方法:

  1. 回調接口tellAnswer(int answer),即學生回答完畢問題以後,老師要作的事情
  2. 問問題方法askQuestion(),即向學生問問題

接着定義一個學生接口,學生固然是解決問題,可是接收一個Callback參數,這樣學生就知道解決完畢問題向誰報告:

/**
 * 學生接口
 */
public interface Student {
    public void resolveQuestion(Callback callback);
}

最後定義一個具體的學生叫Ricky:

/**
 * 一個名叫Ricky的同窗解決老師提出的問題
 */
public class Ricky implements Student {

    @Override
    public void resolveQuestion(Callback callback) {
        // 模擬解決問題
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            
        }
        // 回調,告訴老師做業寫了多久
        callback.tellAnswer(3);
    }

}

寫一個測試類,比較簡單:

/**
 * 回調測試
 */
public class CallbackTest {

    @Test
    public void testCallback() {
        Student student = new Ricky();
        Teacher teacher = new Teacher(student);
        
        teacher.askQuestion();
        
    }
    
}

代碼分析

分析一下上面的代碼,上面的代碼我這裏作了兩層的抽象:

(1)將老師進行抽象

  • 將老師進行抽象以後,對於學生來講,就不須要關心究竟是哪位老師詢問我問題,只要我根據詢問的問題,得出答案,而後告訴提問的老師就能夠了,即便老師換了一茬又一茬,對我學生而言都是沒有任何影響的

(2)將學生進行抽象

  • 將學生進行抽象以後,對於老師這邊來講就很是靈活,由於老師未必對一個學生進行提問,可能同時對Ricky、Jack、Lucy三個學生進行提問,這樣就能夠將成員變量Student改成List<Student>,這樣在提問的時候遍歷Student列表進行提問,而後獲得每一個學生的回答便可

這個例子是一個典型的體現接口做用的例子,之因此這麼說是由於我想到有些朋友可能不太明白接口的好處,不太明白接口好處的朋友能夠重點看一下這個例子,多多理解。

總結起來,回調的核心就是回調方將自己即this傳遞給調用方,這樣調用方就能夠在調用完畢以後告訴回調方它想要知道的信息。回調是一種思想、是一種機制,至於具體如何實現,如何經過代碼將回調實現得優雅、實現得可擴展性比較高,一看開發者的我的水平,二看開發者對業務的理解程度。

上面的例子,可能有人會提出這樣的疑問:

這個例子須要用什麼回調啊,使用同步調用的方式,學生對象回答完畢問題以後直接把回答的答案返回給老師對象不就行了?

這個問題的提出沒有任何問題,能夠從兩個角度去理解這個問題。

    首先,老師不只僅想要獲得學生的答案怎麼辦?可能這個老師是個更喜歡聽學生解題思路的老師,在獲得學生的答案以前,老師更想先知道學生姓名和學生的解題思路,固然有些人能夠說,那我能夠定義一個對象,裏面加上學生的姓名和解題思路不就行了。這個說法在我看來有兩個問題:

(1)若是老師想要的數據愈來愈多,那麼返回的對象得愈來愈大,而使用回調則能夠進行數據分離,將一批數據放在回調方法中進行處理,至於哪些數據依具體業務而定,若是須要增長返回參數,直接在回調方法中增長便可

(2)沒法解決老師但願獲得學生姓名、學生解題思路先於學生回答的答案的問題

所以我認爲簡單的返回某個結果確實沒有必要使用回調而能夠直接使用同步調用,可是若是有多種數據須要處理且數據有主次之分,使用回調會是一種更加合適的選擇,優先處理的數據放在回調方法中先處理掉。

另一個理解的角度則更加劇要,就是標題說的同步回調和異步回調了。例子是一個同步回調的例子,意思是老師向Ricky問問題,Ricky給出答案,老師問下一個同窗,獲得答案以後繼續問下一個同窗,這是一種正常的場景,可是若是我把場景改一下:

老師並不想One-By-One這樣提問,而是同時向Ricky、Mike、Lucy、Bruce、Kate五位同窗提問,讓同窗們本身思考,哪位同窗思考好了就直接告訴老師答案便可。

這種場景至關因而說,同窗思考完畢完畢問題要有一個辦法告訴老師,有兩個解決方案:

  1. 使用Future+Callable的方式,等待異步線程執行結果,這至關於就是同步調用的一種變種,由於其本質仍是方法返回一個結果,即學生的回答
  2. 使用異步回調,同窗回答完畢問題,調用回調接口方法告訴老師答案便可。因爲老師對象被抽象成了Callback接口,所以這種作法的擴展性很是好,就像以前說的,即便老師換了換了一茬又一茬,對於同窗來講,只關心的是調用Callback接口回傳必要的信息便可

先講異步回調Callback,和future模式,這個文章後面給出方案

3.異步回調---CS中的異步回調(java案例)

好比這裏模擬個場景:客戶端發送msg給服務端,服務端處理後(5秒),回調給客戶端,告知處理成功。代碼以下:

    回調接口類:

/**
 * 回調模式-回調接口類
 */
public interface CSCallBack {
    public void process(String status);
}

    模擬客戶端:

/**
 * 回調模式-模擬客戶端類
 */
public class Client implements CSCallBack {

    private Server server;

    public Client(Server server) {
        this.server = server;
    }

    public void sendMsg(final String msg){
        System.out.println("客戶端:發送的消息爲:" + msg);
        new Thread(new Runnable() {
            @Override
            public void run() {
                server.getClientMsg(Client.this,msg);
            }
        }).start();
        System.out.println("客戶端:異步發送成功");
    }

    @Override
    public void process(String status) {
        System.out.println("客戶端:服務端回調狀態爲:" + status);
    }
}

    模擬服務端:

/**
 * 回調模式-模擬服務端類
 */
public class Server {

    public void getClientMsg(CSCallBack csCallBack , String msg) {
        System.out.println("服務端:服務端接收到客戶端發送的消息爲:" + msg);

        // 模擬服務端須要對數據處理
        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("服務端:數據處理成功,返回成功狀態 200");
        String status = "200";
        csCallBack.process(status);
    }
}

    測試類:

/**
 * 回調模式-測試類
 */
public class CallBackTest {
    public static void main(String[] args) {
        Server server = new Server();
        Client client = new Client(server);

        client.sendMsg("Server,Hello~");
    }
}

客戶端:發送的消息爲:Server,Hello~
客戶端:異步發送成功
服務端:服務端接收到客戶端發送的消息爲:Server,Hello~

(這裏模擬服務端對數據處理時間,等待5秒)
服務端:數據處理成功,返回成功狀態 200
客戶端:服務端回調狀態爲:

一步一步分析下代碼,核心總結以下

一、接口做爲方法參數,其實際傳入引用指向的是實現類

二、Client的sendMsg方法中,參數final,由於要被內部類一個新的線程可使用。這裏就體現了異步

三、調用server的getClientMsg(),參數傳入了Client自己(對應第一點)。上面的同步回調也說過了

解惑:回調的應用場景

  1. Windows平臺的消息機制
  2. 異步調用微信接口,根據微信返回狀態對出業務邏輯響應。
  3. Servlet中的Filter(過濾器)是基於回調函數,需容器支持。

補充:其中 Filter(過濾器)和Interceptor(攔截器)的區別,攔截器基因而Java的反射機制,和容器無關。但與回調機制有殊途同歸之妙。

這邊篇幅過長省得你們看的糊塗future模式下篇講,上文的問題個人<碼雲上面的test包>

說實話,我如今面臨的挑戰特別大,要理解的東西愈來愈多.可是我聽過以前一個老師說的話,我以爲頗有道理,心煩意亂的時候寫代碼,寫代碼,寫代碼,失戀了寫代碼,寫代碼.不知道怎麼辦了,寫寫代碼.都能解決

本人qq853089986

相關文章
相關標籤/搜索