同步和异步、阻塞与非阻塞是耳熟能详的几个名词,但是却很难真正理解其含义,虽然也有很多资料以生活中的事例来进行了说明,但还是有一种模糊不清的感觉,其实很多解释都对,但是所站的角度不一样。
这个问题也咨询了一些业界大佬,查阅了一些经典书籍,最终总结如下:
阻塞与非阻塞是一种编程模型;而同步和异步是线程模型;同步和异步、阻塞与非阻塞是不一样的,不能混淆。
同步和异步的区别在于任务执行方和任务发起方是否在同一线程或者进程;同步指任务的发起方和执行方在同一个线程中完成;异步是一种常见的提升吞吐的手段,指任务的发起方和执行方在不同的线程中完成。
而阻塞和非阻塞的差异在于是否阻挡代码继续执行。非阻塞是一种常用的编程模型,由通知状态被动的回调执行,同步或异步执行均可。
在 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:
The request is received and passed via normal filters for authentication etc. to the servlet.
The servlet processes the request parameters and/or content to determine the nature of the request.
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.
The servlet returns without generating a response.
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 处发生了线程切换。
欢迎关注公众号