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,异步支持用到的线程类