上文已经阐述Servlet生命周期是由Servlet容器负责。当浏览器端向服务器端发送第一次请求时,Servlet容器会根据web.xml配置文件里的参数实例化浏览器端请求所对应的类。当浏览器端再次请求该类时,Servlet一般不会再次实例化一个对象,即多个线程访问同一对象。当并发用户上升到一定值时,Servlet实例中的数据可能会变得不一致。因此用Servlet构建web系统是应该注意Servlet线程安全问题。
解决Servlet线程安全有三种解决方案:
一、实现SingleThreadModel接口。该接口制定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,Servlet容器将会为每一个请求创建一个Servlet实例,那么一个实例中的service()方法不会被两个线程同时执行,Servlet线程安全问题便不再存在。这种方法只需要将Servlet类的类头更改为:
......
Public class Concurrent Test extends HttpServlet
implements SingleThreadModel {
private static final long serialVersionUID = 5823950l;
public void service(HttpServletRequest request,
HttpServletResponse response) {
......
}
}
......
二、同步对共享数据的操作。使用synchronized关键字保证一次只有一个线程访问被保护的区段。
......
public synchronized void service(HttpServletRequest request,
HttpServletResponse response) {
......
}
......
三、编程时在Servlet的service方法中避免使用实例变量。下面的代码存在线程安全问题。
......
publicclass AuditGraduationDesignServletextends PlainServlet {
privatestaticfinallongserialVersionUID = 4874878l;
// 该成员在实例化存在线程安全问题
private GraduationDesignDAOgradDAO =null;
// 该成员在实例化存在线程安全问题
private MultipleChoiceDAOmulChDAO =null;
@Override
publicvoid service(HttpServletRequest request, HttpServletResponse response)throws IOException {
......
}
}
Servlet线程安全的三种解决方案比较:
第一种方案实现SingleThreadModel接口将引起大量的系统开销,因为Servlet容器将会为每一个新的请求创建一个单独的Servlet实例。
同步对共享数据的操作,同样会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户端出于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化Servlet中的同步代码。
在Servlet中避免使用实例变量时保证Servlet线程安全的最佳选择。从Java内存结构可知,方法中的临时变量分配在栈中,因为每个线程拥有各自的栈空间,所以不会影响线程的安全。