在Servlet 3.0中,我们可以这么写来达到异步处理:
此时,我们先通过request.startAsync()获取到该请求对应的AsyncContext,然后调用AsyncContext的start()方法进行异步处理,处理完毕后需要调用complete()方法告知Servlet容器。start()方法会向Servlet容器另外申请一个新的线程(可以是从Servlet容器中已有的主线程池获取,也可以另外维护一个线程池,不同容器实现可能不一样),然后在这个新的线程中继续处理请求,而原先的线程将被回收到主线程池中。事实上,这种方式对性能的改进不大,因为如果新的线程和初始线程共享同一个线程池的话,相当于闲置下了一个线程,但同时又占用了另一个线程。
package davenkin.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;@WebServlet(value = "/simpleAsync", asyncSupported = true)public class SimpleAsyncHelloServlet extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {AsyncContext asyncContext = request.startAsync();asyncContext.start(() -> {new LongRunningProcess().run();try {asyncContext.getResponse().getWriter().write("Hello World!");} catch (IOException e) {e.printStackTrace();}asyncContext.complete();});}}
当然,除了调用AsyncContext的start()方法,我们还可以通过手动创建线程的方式来实现异步处理:
package davenkin.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;@WebServlet(value = "/newThreadAsync", asyncSupported = true)public class NewThreadAsyncHelloServlet extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {AsyncContext asyncContext = request.startAsync();Runnable runnable = () -> {new LongRunningProcess().run();try {asyncContext.getResponse().getWriter().write("Hello World!");} catch (IOException e) {e.printStackTrace();}asyncContext.complete();};new Thread(runnable).start();}}
自己手动创建新线程一般是不被鼓励的,并且此时线程不能重用。因此,一种更好的办法是我们自己维护一个线程池。这个线程池不同于Servlet容器的主线程池,如下图: 
package davenkin.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.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;@WebServlet(value = "/threadPoolAsync", asyncSupported = true)public class ThreadPoolAsyncHelloServlet extends HttpServlet {private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {AsyncContext asyncContext = request.startAsync();executor.execute(() -> {new LongRunningProcess().run();try {asyncContext.getResponse().getWriter().write("Hello World!");} catch (IOException e) {e.printStackTrace();}asyncContext.complete();});}}
Servlet 3.0对请求的处理虽然是异步的,但是对InputStream和OutputStream的IO操作却依然是阻塞的,对于数据量大的请求体或者返回体,阻塞IO也将导致不必要的等待。因此在Servlet 3.1中引入了非阻塞IO(参考下图红框内容),通过在HttpServletRequest和HttpServletResponse中分别添加ReadListener和WriterListener方式,只有在IO数据满足一定条件时(比如数据准备好时),才进行后续的操作。
我们为ServletInputStream添加了一个ReadListener,并在ReadListener的onAllDataRead()方法中完成了长时处理过程
package davenkin.servlet;import javax.servlet.AsyncContext;import javax.servlet.ReadListener;import javax.servlet.ServletException;import javax.servlet.ServletInputStream;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.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;@WebServlet(value = "/nonBlockingThreadPoolAsync", asyncSupported = true)public class NonBlockingAsyncHelloServlet extends HttpServlet {private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {AsyncContext asyncContext = request.startAsync();ServletInputStream inputStream = request.getInputStream();inputStream.setReadListener(new ReadListener() {@Overridepublic void onDataAvailable() throws IOException {}@Overridepublic void onAllDataRead() throws IOException {executor.execute(() -> {new LongRunningProcess().run();try {asyncContext.getResponse().getWriter().write("Hello World!");} catch (IOException e) {e.printStackTrace();}asyncContext.complete();});}@Overridepublic void onError(Throwable t) {asyncContext.complete();}});}}
