package com.jadyer.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; 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 com.jadyer.thread.HelloAsyncThread; /** * Servlet3.0新特性之异步支持 * @see ------------------------------------------------------------------------------------------------- * @see 异步实现概述 * @see 1)Servlet2.5中也可以单独启动一个线程去执行耗时的任务,接着Servlet会继续往下执行 * @see 执行完最后一行代码时,Servlet就会把响应输出给请求方,而那个线程的任何执行结果都不会反映给请求方 * @see 2)Servlet3.0中的异步支持也是单独启动线程执行耗时任务,当Servlet执行到最后一行代码时 * @see Servlet3.0提供了一些机制会判断那个线程是否执行完毕,直到线程执行完毕后才会响应所有结果给请求方 * @see (故本例中浏览器访问Servlet时,它会先转圈即正在加载...,直到5秒后异步线程执行完毕,浏览器才显示出了四行时间) * @see 3)若不单独启动线程去执行耗时任务,而是将耗时操作串行放到Servlet方法中执行的话,也是不可取的 * @see 因为Servlet容器会管理着一个线程池,处理我们请求的Servlet所依附的线程都是来自于这个线程池的 * @see 如果某个操作过于耗时,那么线程池里面的资源就会被占用掉,所以Servlet3.0就有了异步支持 * @see ------------------------------------------------------------------------------------------------- * @see 异步实现步骤 * @see 1)在Servlet中开启异步支持(默认是不支持异步的),@WebServlet(asyncSupported=true) * @see 2)在Servlet中启动一个异步的上下文对象,AsyncContext context = request.startAsync(); * @see 3)单独创建一个线程执行耗时操作,注意耗时操作执行完毕要调用AsyncContext.complete(); * @see 4)在Servlet中启动耗时操作的线程 * @see ------------------------------------------------------------------------------------------------- * @create Jun 23, 2013 5:47:55 PM * @author 玄玉<http://blog.csdn.net/jadyer> */ @WebServlet(urlPatterns={"/hello/async"}, asyncSupported=true) public class HelloAsyncServet extends HttpServlet { private static final long serialVersionUID = 3314056681141922826L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取当前方法的名字 String methodName = Thread.currentThread().getStackTrace()[1].getMethodName(); PrintWriter out = response.getWriter(); out.println(methodName + " starts: " + new Date()); out.flush(); System.out.println("第一步mark"); //启动一个异步操作的上下文对象AsyncContext AsyncContext context = request.startAsync(); //添加一个监听器(奇怪:AsyncListener竟然没有适配器) context.addListener(new AsyncListener(){ @Override public void onComplete(AsyncEvent asyncEvent) throws IOException { PrintWriter out = asyncEvent.getSuppliedResponse().getWriter(); out.println("async succes: " + new Date()); out.flush(); /** * 在这里关闭流(此处为重点) * @see ------------------------------------------------------------------------------------- * @see 总结:自打请求进入doGet()方法中开始,直到请求方收到响应为止 * @see 整个过程中的任意位置所获得的PrintWriter和ServletResponse对象都是相同的 * @see 说明:在5个步骤mark位置,均执行System.out.println(PrintWriter或ServletResponse对象); * @see 我们会发现在五个mark位置的这俩对象的hashCode和内存地址都是相同的 * @see 并且:第一个mark处声明的PrintWriter对象不能在第二个mark处关闭,只能在第4个或第5个mark处关闭 * @see 否则异步支持就会失败 * @see 补充:第5个mark处的out.println()也会作为响应结果给请求方,这点要注意 * @see ------------------------------------------------------------------------------------- */ out.close(); System.out.println("第五步mark"); } @Override public void onError(AsyncEvent arg0) throws IOException {} @Override public void onStartAsync(AsyncEvent arg0) throws IOException {} @Override public void onTimeout(AsyncEvent arg0) throws IOException {} }); //也可以用这种方法启动异步线程 //context.start(new HelloAsyncThread(context)); new Thread(new HelloAsyncThread(context)).start(); out.println(methodName + " ends: " + new Date()); out.flush(); System.out.println("第二步mark"); } }
最后是异步支持用到的线程类
package com.jadyer.thread; import java.io.PrintWriter; import java.util.Date; import javax.servlet.AsyncContext; /** * Servlet3.0新特性之异步支持用到的线程类 * @create Jun 23, 2013 6:05:55 PM * @author 玄玉<http://blog.csdn.net/jadyer> */ public class HelloAsyncThread implements Runnable { private AsyncContext context; public HelloAsyncThread(AsyncContext context){ this.context = context; } @Override public void run() { try { PrintWriter out = context.getResponse().getWriter(); out.println("async starts: " + new Date()); out.flush(); System.out.println("第三步mark"); //模拟耗时操作,让线程睡5s Thread.sleep(5000); out.println("async ends: " + new Date()); out.flush(); System.out.println("第四步mark"); //调用complete()表明异步处理已完成 //它会让Servlet知道异步处理已完毕,否则Servlet还在傻傻等待,浪费时间 context.complete(); } catch (Exception e) { e.printStackTrace(); } } } 来源http://www.cnblogs.com/Augusdi/archive/2013/06/24/3153175.html本文链接地址: 启用了异步支持的Servlet,异步支持用到的线程类