Servlet容器默认是采用单实例多线程的方式处理多个请求的:
1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一个Servlet实例);
2.容器初始化化Servlet主要就是读取配置文件(例如tomcat,可以通过servlet.xml的<Connector>设置线程池中线程数目,初始化线程池通过web.xml,初始化每个参数值等等。
3.当请求到达时,Servlet容器通过调度线程(Dispatchaer Thread) 调度它管理下线程池中等待执行的线程(Worker Thread)给请求者;
4.线程执行Servlet的service方法;
5.请求结束,放回线程池,等待被调用;
(注意:避免使用实例变量(成员变量),因为如果存在成员变量,可能发生多线程同时访问该资源时,都来操作它,照成数据的不一致,因此产生线程安全问题)
不是单例,只能说web容器对servlet实例化了一次。servlet只是一个普通的类,它也有自已的构造函数,甚至可以用new的方式new出N多个servlet的实例,但它能正常地处理web请求,就需要交给web服务器(或者叫servlet/jsp容器)来进行管理,比如说tomcat,tomcat通过配置文件获取映射信息,然后只会在第一次生成servlet的实例,并把它缓存起来,下次再次请求,同样是取的这个实例,所以它的之前状态还是被保存起来的,所以共享的数据如果不是线程安全的,会出问题,所以别把数据用成员属性进行保存,别让servlet有状态。
在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 necessary if (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 wait if (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 pool if (!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的Servlet singleThreadModel = true; } initServlet(servlet); } finally { } return servlet; }
原文:https://blog.51cto.com/u_15241951/2861799