在百度贴吧看到这样一个问题,于是自己给了答复,感觉这个问题还是蛮有意思的,所有记录下来。贴吧地址:
http://zhidao.baidu.com/question/209371363.html
问题:
听说实现SingleThreadModel,可以使容器对客户端的每个请求创建新的servlet实例,但是我实现了那个接口之后,还是不行阿。 比如,我给servlet增加属性int count = 0;在doGet()方法里System.out.println(count++);为什么不是每次访问都输出0,而是递增呢?回答:
其实是有实例的,当一个servlet实现了SingleThreadModel接口之后就会有一个Stack来保存这些实例。 至于为什么每次访问都是递增的。是因为当请求的实例数大于当前实例数的时候才会重新去加载个实例,然后放到Stack中,很明显,楼主第一次请求完后该实例被回收到Stack中,第二次请求时发现Stack中有实例供调用,当然就用Stack中的实例了而不会去重新加载了。
可以查看org.apache.catalina.core.StandardWrapper类的allocate方法。
public Servlet allocate() throws ServletException {
if (debug >= 1)
log("Allocating an instance");
// If we are currently unloading this servlet, throw an exception
if (unloading)
throw new ServletException
(sm.getString("standardWrapper.unloading", getName()));
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null) {
synchronized (this) {
if (instance == null) {
try {
instance = loadServlet();
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
throw new ServletException
(sm.getString("standardWrapper.allocate"), e);
}
}
}
}
if (!singleThreadModel) {
if (debug >= 2)
log(" Returning non-STM instance");
countAllocated++;
return (instance);
}
}
synchronized (instancePool) {
while (countAllocated >= nInstances) {//请求的数量大于当前的实例数才会加载新的实例的
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());//在这加载
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
throw new ServletException
(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
;
}
}
}
if (debug >= 2)
log(" Returning allocated STM instance");
countAllocated++;
return (Servlet) instancePool.pop();
}
}
当一个实例调用完之后会被回收到Stack中
public void deallocate(Servlet servlet) throws ServletException {
// If not SingleThreadModel, no action is required
if (!singleThreadModel) {
countAllocated--;
return;
}
// Unlock and free this instance
synchronized (instancePool) {
countAllocated--;
instancePool.push(servlet);
instancePool.notify();
}
}
从以上的代码可以看出,SingleThreadModel接口使Servlet程序员产生虚假的安全感,认为它是线程安全的。实际上该接口并不能避免同步而产生的问题,如访问静态类变量或该servlet以外的类或变量,即使访问该servlet类自身的属性也得很小心(如那个问题所描述的问题)。所以Servlet 2.4中已经废弃了该接口,不建议写servlet的时候继承该接口。因而对于大多数的servlet来说都是单例的。