聊聊同步和異步(2)

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


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

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

2、使用Java編寫
java_async.png

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

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

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

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è)務(wù)處理組件(耗時)
            new Thread(new Runnable() {
                public void run() {

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

                }
            }).start();

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

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

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

class ServiceProcess{

        public void dealService(String information,ServletProcess servletProcess){

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

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

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

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

servlet_async.png

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

@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服務(wù)器端要向客戶端發(fā)送消息,可能發(fā)送這個消息非常耗時,那么此時會造成服務(wù)器端程序阻塞,使得服務(wù)器端的處理性能急劇下降。因此,可以對消息的發(fā)送進行異步處理。即WebSocket對應(yīng)的Java API中的Async對象向服務(wù)器端發(fā)送消息時,調(diào)用send方法,其只是通知send方法,立即返回。異步線程(使用Future接口)來負責(zé)向客戶端發(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("服務(wù)器收到的信息為:" + message);

        session.getAsyncRemote().sendText(SendInformationAsync.sendInfo(), new SendHandler() {
          //服務(wù)器向客戶端發(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è)務(wù)組件service。此時用注冊一個事件的監(jiān)聽器,即事件發(fā)生之后,調(diào)用callBack方法。用戶在servlet方法中調(diào)用service方法,立即返回,并不知道service方法中的業(yè)務(wù)何時處理完成。利用事件監(jiān)聽器,在service方法中的業(yè)務(wù)處理完成之后,出發(fā)剛才注冊的事件,即可調(diào)用callBack方法。

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

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

 function service(command){
        //業(yè)務(wù)組件
        setTimeout(function(){
            //處理業(yè)務(wù)
            console.log("開始處理業(yè)務(wù)");
            console.log(command);
            console.log("處理業(yè)務(wù)完畢")
           //觸發(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è)務(wù)組件,注冊一個異步線程的監(jiān)聽器。該監(jiān)聽器主要監(jiān)聽四個事件,success,timeout,error,startAsync。Web容器的主線程調(diào)用業(yè)務(wù)組件,則開啟一個異步線程,立即返回。主線程并不知道異步線程是否已經(jīng)完成了業(yè)務(wù)處理。因此,異步線程在完成了業(yè)務(wù)處理之后,AsyncContext對象調(diào)用complete或者dispatch方法,即觸發(fā)了success事件。觸發(fā)該事件之后,立即調(diào)用監(jiān)聽器的onSuccess方法,表明異步線程已經(jīng)完成業(yè)務(wù)處理。

@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è)務(wù)處理完成之后,調(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();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容