聊聊同步和異步(2)

傳遞回調(diào)函數(shù)


1、使用Javascript編寫
function complete(information){
        console.log(information);
}
function servlet(command){
        console.log("調(diào)用業(yè)務組件")
        service(command,complete);
}
function service(command,callBack){
        setTimeout(function(){
            console.log(command);
            callBack("業(yè)務組件完成調(diào)用");
        },1000);
}
//用戶調(diào)用業(yè)務處理
servlet("select * from user_table where username = wangbinghua");
javascript_async_flow.png

Servlet() 作為用戶調(diào)用的方法酬凳,其通知業(yè)務組件service(),并不知道業(yè)務組件何時調(diào)用完畢琐谤,因此將complete()回調(diào)函數(shù)作為參數(shù)檐蚜,調(diào)用業(yè)務組件魄懂。等待業(yè)務組件處理完畢業(yè)務之后,再次調(diào)用complete()函數(shù)闯第,表明已經(jīng)完成業(yè)務調(diào)用市栗。

2、使用Java編寫
java_async.png

在java中無法傳遞函數(shù)咳短,因此將接口作為參數(shù)進行傳遞填帽,從而達到傳遞函數(shù)的目的。

interface CallBack{
        //回調(diào)函數(shù)
        public void callBack(String result);
}

用戶主線程咙好,調(diào)用業(yè)務組件篡腌。而業(yè)務主線程驅(qū)動ServletProcess類的invokeService方法,在調(diào)用業(yè)務組件的同時勾效,開啟一條線程來處理業(yè)務邏輯嘹悼。因主線程驅(qū)動的ServletProcess類無法得知異步線程何時才能完成業(yè)務邏輯處理。所以层宫,將回調(diào)函數(shù)所在的接口作為參數(shù)傳遞給業(yè)務邏輯所處的異步線程杨伙。在異步線程完成之后,再次調(diào)用callBack方法卒密,表明業(yè)務邏輯完成處理缀台。

class ServletProcess implements CallBack{

        private ServiceProcess serviceProcess;
        public ServletProcess(ServiceProcess serviceProcess){
            this.serviceProcess = serviceProcess;
        }

        public void dealOtherRequest(){
            System.out.println("接受其他用戶的請求");
        }
        public void invokeService(final String information){

            System.out.println("用戶線程開始:" + new Date());

            //開啟異步線程調(diào)用業(yè)務處理組件(耗時)
            new Thread(new Runnable() {
                public void run() {

                    System.out.println("異步線程開始");
                    //調(diào)用業(yè)務組件
                    serviceProcess.dealService(information,ServletProcess.this);

                }
            }).start();

            //接受其他用戶的請求
            this.dealOtherRequest();
            System.out.println("用戶線程結(jié)束:" + new Date());
        }

        //業(yè)務組件完成后,調(diào)用該方法
        public void callBack(String result) {
            System.out.println(result);
        }
}

異步線程驅(qū)動的業(yè)務組件:

class ServiceProcess{

        public void dealService(String information,ServletProcess servletProcess){

            //處理業(yè)務
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("處理業(yè)務:" + information);
            System.out.println("異步線程結(jié)束");

            //處理完畢之后哮奇,通知ServletProcess組件
            servletProcess.callBack("處理數(shù)據(jù)膛腐,渲染頁面");
        }
}
3、類比Servlet異步處理
線程池.png

如果使用同步處理鼎俘,那么用戶每次請求一次哲身,就需要從線程池中獲取一個線程進行處理用戶的請求。那么在同步的條件下贸伐,都是由這一個線程同時進行請求處理和業(yè)務處理勘天。如果業(yè)務處理比較耗時,那么線程就會進行阻塞捉邢。此時脯丝,有更多的用戶進行請求,線程池中的線程在極端情況下全部阻塞伏伐,那么就無法處理用戶的請求宠进。用戶必須等待之前的業(yè)務處理的完成,很大程度上影響系統(tǒng)的吞吐量藐翎。因此提倡采用servlet的異步處理材蹬。

servlet通知完耗時業(yè)務組件處理業(yè)務之后实幕,馬上返回到線程池中,而不進行等待堤器。后續(xù)的操作由回調(diào)函數(shù)或者事件監(jiān)聽器完成昆庇。這樣,接下來更多的用戶請求闸溃,就會充分利用線程池中的線程整吆。

servlet_async.png

AsyncServlet異步調(diào)用業(yè)務組件處理業(yè)務邏輯,則其通知AsyncTask異步線程調(diào)用業(yè)務組件辉川,然后立即返回掂为。與此同時,Web容器線程將AsyncContext對象傳遞給AsyncTask異步線程员串。當異步線程處理業(yè)務完畢之后,將調(diào)用AsyncContext對象的complete方法或者dispach方法昼扛,表明業(yè)務處理完畢寸齐。

@WebServlet(name = "Servlet",urlPatterns = {"/us"},asyncSupported = true)
public class AsyncServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        AsyncContext asyncContext = request.startAsync();
        asyncContext.start(new AsyncTask(asyncContext));
    }
}

class AsyncTask implements Runnable{

    private AsyncContext asyncContext;
    public AsyncTask(AsyncContext asyncContext){
        this.asyncContext = asyncContext;
    }

    public void run() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("deal some things!");
        this.asyncContext.dispatch("/async.jsp");
//        this.asyncContext.complete();
    }
}

3、類比WebSocket異步處理
websocket_async.png

**
WebSocket的java服務器端要向客戶端發(fā)送消息抄谐,可能發(fā)送這個消息非常耗時渺鹦,那么此時會造成服務器端程序阻塞,使得服務器端的處理性能急劇下降蛹含。因此毅厚,可以對消息的發(fā)送進行異步處理。即WebSocket對應的Java API中的Async對象向服務器端發(fā)送消息時浦箱,調(diào)用send方法吸耿,其只是通知send方法,立即返回酷窥。異步線程(使用Future接口)來負責向客戶端發(fā)送消息咽安,此時容器主線程并不知道什么時候異步線程可以發(fā)送消息完畢。因此蓬推,在使用異步線程調(diào)用send方法的同時妆棒,將SendHandler接口傳遞給異步線程。當異步線程發(fā)送消息完畢時沸伏,則調(diào)用SendHandler接口的onResult方法糕珊,表明異步線程已經(jīng)發(fā)送消息完畢,從而讓容器主線程感知到毅糟。
**

 @OnMessage
  public void receiveMessage(Session session,String message,@PathParam("loginName") String loginName)  
          throws IOException {

        System.out.println("服務器收到的信息為:" + message);

        session.getAsyncRemote().sendText(SendInformationAsync.sendInfo(), new SendHandler() {
          //服務器向客戶端發(fā)送數(shù)據(jù)完畢之后红选,則調(diào)用SendHandler接口的onResult方法
            public void onResult(SendResult result) {
                if(result.isOK()){
                    System.out.println("信息發(fā)送完畢");
                }
            }
        });
 }

使用監(jiān)聽器


1、使用Javascript編寫
javascript_listener.png

用戶主線程調(diào)用servlet方法留特,而servlet方法調(diào)用業(yè)務組件service纠脾。此時用注冊一個事件的監(jiān)聽器得哆,即事件發(fā)生之后,調(diào)用callBack方法钧敞。用戶在servlet方法中調(diào)用service方法胳蛮,立即返回,并不知道service方法中的業(yè)務何時處理完成慧脱。利用事件監(jiān)聽器渺绒,在service方法中的業(yè)務處理完成之后,出發(fā)剛才注冊的事件菱鸥,即可調(diào)用callBack方法宗兼。

function servlet(command){
        //調(diào)用業(yè)務組件
        console.log("調(diào)用業(yè)務組件");
        service(command);
}

function callBack(){
        console.log("渲染頁面");
}

 function service(command){
        //業(yè)務組件
        setTimeout(function(){
            //處理業(yè)務
            console.log("開始處理業(yè)務");
            console.log(command);
            console.log("處理業(yè)務完畢")
           //觸發(fā)事件
           $("#event").trigger("click");
       },1000);
}

$("#event").on("click",callBack);
servlet("select * from user_table where username = wangbinghua");
2、類比servlet異步處理
servlet_listener.png

在Web容器主線程中氮采,調(diào)用業(yè)務組件殷绍,注冊一個異步線程的監(jiān)聽器。該監(jiān)聽器主要監(jiān)聽四個事件鹊漠,success主到,timeout,error躯概,startAsync登钥。Web容器的主線程調(diào)用業(yè)務組件,則開啟一個異步線程娶靡,立即返回牧牢。主線程并不知道異步線程是否已經(jīng)完成了業(yè)務處理。因此姿锭,異步線程在完成了業(yè)務處理之后塔鳍,AsyncContext對象調(diào)用complete或者dispatch方法,即觸發(fā)了success事件呻此。觸發(fā)該事件之后献幔,立即調(diào)用監(jiān)聽器的onSuccess方法,表明異步線程已經(jīng)完成業(yè)務處理趾诗。

@WebServlet(name = "Servlet",urlPatterns = {"/us"},asyncSupported = true)
public class AsyncServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setCharacterEncoding("utf-8");

        final AsyncContext asyncContext = request.startAsync();

        //注冊事件監(jiān)聽器
        asyncContext.addListener(new AsyncListener() {

            //異步線程業(yè)務處理完成之后蜡感,調(diào)用該方法
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                try {
                    asyncContext.getRequest().getRequestDispatcher("/async.jsp").
                            forward(asyncContext.getRequest(),asyncContext.getResponse());
                } catch (ServletException e) {
                    e.printStackTrace();
                }
                System.out.println("異步線程完成");
            }

            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onTimeout");
            }

            public void onError(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onError");
            }

            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onStartAsync");
            }
        });
        asyncContext.start(new AsyncTask(asyncContext));

    }
}

class AsyncTask implements Runnable{

    private AsyncContext asyncContext;
    public AsyncTask(AsyncContext asyncContext){
        this.asyncContext = asyncContext;
    }

    public void run() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("deal some things!");
//        this.asyncContext.dispatch("/async.jsp");
        this.asyncContext.complete();
    }
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恃泪,隨后出現(xiàn)的幾起案子郑兴,更是在濱河造成了極大的恐慌,老刑警劉巖贝乎,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件情连,死亡現(xiàn)場離奇詭異,居然都是意外死亡览效,警方通過查閱死者的電腦和手機却舀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門虫几,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挽拔,你說我怎么就攤上這事辆脸。” “怎么了螃诅?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵啡氢,是天一觀的道長。 經(jīng)常有香客問我术裸,道長倘是,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任袭艺,我火速辦了婚禮搀崭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猾编。我一直安慰自己门坷,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布袍镀。 她就那樣靜靜地躺著,像睡著了一般冻晤。 火紅的嫁衣襯著肌膚如雪苇羡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天鼻弧,我揣著相機與錄音设江,去河邊找鬼。 笑死攘轩,一個胖子當著我的面吹牛叉存,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播度帮,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼歼捏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了笨篷?” 一聲冷哼從身側(cè)響起瞳秽,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎率翅,沒想到半個月后练俐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡冕臭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年腺晾,在試婚紗的時候發(fā)現(xiàn)自己被綠了燕锥。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡悯蝉,死狀恐怖归形,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泉粉,我是刑警寧澤连霉,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站嗡靡,受9級特大地震影響跺撼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜讨彼,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一歉井、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哈误,春花似錦哩至、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至重荠,卻和暖如春箭阶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戈鲁。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工仇参, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人婆殿。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓诈乒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親婆芦。 傳聞我的和親對象是個殘疾皇子怕磨,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 從三月份找實習到現(xiàn)在,面了一些公司消约,掛了不少癌压,但最終還是拿到小米、百度荆陆、阿里滩届、京東、新浪、CVTE帜消、樂視家的研發(fā)崗...
    時芥藍閱讀 42,194評論 11 349
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理棠枉,服務發(fā)現(xiàn),斷路器泡挺,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 1. Java基礎部分 基礎部分的順序:基本語法辈讶,類相關的語法,內(nèi)部類的語法娄猫,繼承相關的語法贱除,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • 這世界從來都不缺乏創(chuàng)意媳溺,缺乏的只是將創(chuàng)意付諸實際的勇氣每一部被稱作概念機的設計月幌,都傾注了消費者對生產(chǎn)廠商的期許,那...
    24e2f6668318閱讀 234評論 0 0
  • 有人問 為什么我遇不到能夠堅定的愛我的人 有時候只是他剛好需要 我也剛好在 這世間的愛情哪有那么多一往而深 深愛總...
    未曾饒過歲月閱讀 167評論 0 0