Servlet到底是单例还是多例你了解吗?

为一个Java Web开发者,你一定了解和学习过Servlet。或许还曾在面试中被问到过Servelt是单例还是多例这个问题。

遇到这个问题,你是否曾深入了解过,还是百度或者Google了一下,得到答案就OK了呢?

我们今天从Servlet规范及Tomcat源码实现的角度,分析下这个问题。

在Servlet规范中,对于Servlet单例与多例定义如下:

“Deployment Descriptor”, controls how the servlet container provides instances of the servlet.For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.

上面规范提到,
如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。

而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。实例有多少呢,这里没细说。

下面再从Tomcat的源码中找寻下具体的参考实现是什么样子的。以下代码来源于Tomcat的StandardWrapper类。我把其中不太相关的部分做了删除。

public Servlet allocate() throws ServletException {boolean newInstance = false;if (!singleThreadModel) {// Load and initialize our instance if necessaryif (instance == null) {synchronized (this) {if (instance == null) {try {instance = loadServlet();} catch (ServletException e) {}}}}if (singleThreadModel) {if (newInstance) {synchronized (instancePool) {instancePool.push(instance); //如果实现STM接口,就放到一个栈里nInstances++;}}} else {if (!newInstance) {countAllocated.incrementAndGet();}return (instance);}}synchronized (instancePool) {while (countAllocated.get() >= nInstances) {// Allocate a new instance if possible, or else waitif (nInstances < maxInstances) {try {instancePool.push(loadServlet());nInstances++;} catch (ServletException e) {}} else {try {instancePool.wait();} catch (InterruptedException e) {// Ignore}} }countAllocated.incrementAndGet();return instancePool.pop();}}
/*** Load and initialize an instance of this servlet, if there is not already* at least one initialized instance. This can be used, for example, to* load servlets that are marked in the deployment descriptor to be loaded* at server startup time.*/public synchronized Servlet loadServlet() throws ServletException {// Nothing to do if we already have an instance or an instance poolif (!singleThreadModel && (instance != null))return instance; //注意此处,如果存在实例就直接返回Servlet servlet;try {InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();try {servlet = (Servlet) instanceManager.newInstance(servletClass);} catch (ClassCastException e) {}if (servlet instanceof SingleThreadModel) {if (instancePool == null) {instancePool = new Stack<>();} //此处,使用Stack存放STM的ServletsingleThreadModel = true;}initServlet(servlet);} finally {}return servlet;}

那一个实现了SingleThreadModel接口的Servlet,一般会初始化多少个实例呢?
StandardWrapper类中有两个属性,其中maxInstance初始为20。所以上面的问题就有了答案。

/** * Does this servlet implement the SingleThreadModel interface? */protected volatile boolean singleThreadModel = false;/** * Maximum number of STM instances. */protected int maxInstances = 20;

由于SingleThreadModel已经声明为废弃,官方不建议使用。我们这里只是让大家了解下。

总结下,一个Servlet究竟有几个实例呢?受如下几个原因影响:
是否在分布式环境中部署
是否实现SingleThreadModel,如果实现则最多会创建20个实例
在web.xml中声明了几次,即使同一个Servlet,如果声明多次,也会生成多个实例。

(0)

相关推荐