基本上Servlet容器会为每一个Servlet注册名称实例化一个对象,来自客户端的请求会以一个执行绪来存取这个对象,如果有多个请求同时到达,就会有多个执行绪存取同一个Servlet实例,而这所引发的就是执行绪的安全问题。
如果您的Servlet实例之方法都是使用区域变量,这并不会有太大的问题,每一个执行绪对方法的呼叫会使用自己的区域变量,然而如果您的 Servlet方法中有共享一些field成员或是静态成员,在多个执行绪同时存取时,就会有执行绪安全问题,例如一个简单的计数器问题:
ServletDemo.javapackage onlyfun.caterpillar; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ServletDemo extends HttpServlet { private int count = 0; public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { PrintWriter out = res.getWriter(); count++; out.println("This servlet is accessd " + count + "times"); } }
在上面这个Servlet中,由于count是field成员,在Servlet载入后,只有一个Servlet实例,每个请求以一个执行绪来存取这个实 例,有可能发生在前一个执行绪正在执行out.println(),而后一个执行绪正好执行count++,这会导致 count的计数不连续的情况发生,这个情况在这个例子中并不致于引发多大的问题,但它所代表的是多执行绪存取同一个Servlet的执行绪安全问题。
要求执行绪安全最基本的,就是对共享资源作同步化,例如对doGet()使用synchronized关键词:
public synchronized void doGet(HttpServletRequest req, HttpServletResponse res)或者是:
.... PrintWriter out = res.getWriter(); synchronized(this) { count++; out.println("This servlet is accessd " + count + "times"); } ....
同步化所带来的是延迟,当同步化的区块被锁定时,其它的执行绪必须等待锁定的解除,这所代表的就是对使用者请求的延迟,在服务器上这点延迟在客户端多时就 会导致使用者必须花费长时间来等待响应,可以将同步化区块尽量缩小来减少延迟,或者是完全使用区域变量,使得执行绪之间不共享某些资源,然后不共享资源所 带来的,就是某些信息将无法实现持续性,为了实现持续性可能必须额外花费一些手续。
回顾一下JSP中在<%! 与 %>之间宣告的变量,在生成Servlet之后,就是生成一个field成员,所以在<%! 与 %>之间宣告变量,也必须小心执行绪安全问题,通常并不建议这么宣告变量,而是在<% 与 %>之间宣告变量,因为在转为Servlet之间,它是_jspService()中的一个区域变量,即使在多个客户端执行绪同时存取时,也不致于 发生执行绪共享资源的问题。