用最簡單的方式理解同步和異步、阻塞與非阻塞

同步和異步、阻塞與非阻塞是耳熟能詳的幾個名詞,但是卻很難真正理解其含義,雖然也有很多資料以生活中的事例來進行了說明,但還是有一種模糊不清的感覺,其實很多解釋都對,但是所站的角度不一樣。

這個問題也諮詢了一些業界大佬,查閱了一些經典書籍,最終總結如下:

阻塞與非阻塞是一種編程模型;而同步和異步是線程模型;同步和異步、阻塞與非阻塞是不一樣的,不能混淆。

同步和異步的區別在於任務執行方和任務發起方是否在同一線程或者進程;同步指任務的發起方和執行方在同一個線程中完成;異步是一種常見的提升吞吐的手段,指任務的發起方和執行方在不同的線程中完成。

而阻塞和非阻塞的差異在於是否阻擋代碼繼續執行。非阻塞是一種常用的編程模型,由通知狀態被動的回調執行,同步或異步執行均可。

在 Servlet3.0 之後提供了異步化的支持:

The asynchronous processing of requests is introduced to allow the thread may return to the container and perform other tasks. When asynchronous processing begins on the request, another thread or callback may either generate the response and call complete or dispatch the request so that it may run in the context of the container using the AsyncContext.dispatch method. A typical sequence of events for asynchronous processing is:

  1. The request is received and passed via normal filters for authentication etc. to the servlet.

  2. The servlet processes the request parameters and/or content to determine the nature of the request.

  3. The servlet issues requests for resources or data, for example, sends a remote web service request or joins a queue waiting for a JDBC connection.

  4. The servlet returns without generating a response.

  5. After some time, the requested resource becomes available, the thread handling that event continues processing either in the same thread or by dispatching to a resource in the container using the AsyncContext.

同時 Servlet3.0 規範也簡單舉例說明了一個異步事件的處理順序,第四步就提到 servlet 會返回但是不產生響應。看一個 Servlet 異步非阻塞的例子(我這裏使用的是 Spring Boot 項目,需要在啓動類上標註 @ServletComponentScan 註解):

package com.example.simplespringboot.servlet;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

/**
 * @author Dongguabai
 * @Description
 * @Date 創建於 2020-06-29 13:15
 */
@WebServlet(urlPatterns = "/aa",asyncSupported = true)
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("start");
        //開啓異步上下文
        AsyncContext asyncContext = req.startAsync();
        print(1);
        asyncContext.start(()->{
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException ignored) {
            }
            print(2);
            try {
                resp.getWriter().println(new Date().toLocaleString());
            } catch (IOException ignored) {
            }
            asyncContext.complete();
        });
        System.out.println("end");
        print(3);


    }

    private void print(int i){
        System.out.println(i+"--->"+Thread.currentThread().getName());
    }
}

執行:
在這裏插入圖片描述

控制檯輸出:

start
1--->http-nio-8080-exec-2
end
3--->http-nio-8080-exec-2
2--->http-nio-8080-exec-3

可以發現在 1 和 3 處程序並未阻塞,在 2 處發生了線程切換。

References

  • 《UNIX網絡編程 卷1》
  • https://download.oracle.com/otndocs/jcp/servlet-3_1-fr-spec/index.html

歡迎關注公衆號
​​​​​​在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章