如题,是吗?首先我们得搞清楚啥是单例。一聊起单例,条件反射的第一个想到的自然是单例模式。单例模式的定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供。如果按照Java中单例的定义,那么当Servlet没有实现SingleThreadModel接口时,它确实是单例的。

  但如果往细处再进一步深究的话,又好像不是那么一回事了。还是先看单例模式,举个例子,Sigleton类是个单例,它为了保证实例的唯一性,坚决不给别人实例化它的机会,那么它会把构造器定义为私有的(private),这样其他人就没法new出它的实例了(但其实通过反射还是可以实例化的,这里不展开)。而Servlet本身是一个接口,我们一般用的是HttpServlet,它继承了GenericServlet,而GenericServlet实现了Servlet。虽然HttpServlet是抽象类,然而它却有自己的构造器,而且是公有的(public)。我们知道子类初始化实例时必然先调用父类的构造器,也就是如果我现在有一个DemoServlet,那么实例化它将执行HttpServlet的构造器。当然了,父亲的父亲GenericServlet的构造器也会加载,而且这位祖父的构造器也是公有的。如此看来,单例模式中的私有构造器与Servlet中的公有构造器明显匹配不上了。

  综上所述,我还是偏向于广义上的范畴,只要满足在整个系统中仅有一个实例,就认为它是单例。回到最先前的那句话:当Servlet没有实现SingleThreadModel接口时,它才是单例的。虽然SingleThreadModel被标记为过期的了,但仍可以用。如果实现该接口,那么每次请求相同的Servlet,将创建一个新的实例。说白了就跟CGI一样了,每次web请求都起一个进程来处理。

  Servlet本身是规范,它需要实现了这组规范的Servlet容器来提供web能力。一提到servlet容器,条件反射的第一个想到的自然是Tomcat。Tomcat才是去实例化Servlet的那个他。而Tomcat里执行实例化Servlet的类叫StandardWrapper,它有个loadServlet的方法:

    /**
* 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.
* @return the loaded Servlet instance
* @throws ServletException for a Servlet load error
*/
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; PrintStream out = System.out;
if (swallowOutput) {
SystemLogHandler.startCapture();
} Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
} InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
unavailable(null); // Added extra log statement for Bugzilla 36630:
// https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
if(log.isDebugEnabled()) {
log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
} // Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.instantiate", servletClass), e);
} if (multipartConfigElement == null) {
MultipartConfig annotation =
servlet.getClass().getAnnotation(MultipartConfig.class);
if (annotation != null) {
multipartConfigElement =
new MultipartConfigElement(annotation);
}
} // Special handling for ContainerServlet instances
// Note: The InstanceManager checks if the application is permitted
// to load ContainerServlets
if (servlet instanceof ContainerServlet) {
((ContainerServlet) servlet).setWrapper(this);
} classLoadTime=(int) (System.currentTimeMillis() -t1); if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<>();
}
singleThreadModel = true;
} initServlet(servlet); fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1;
} finally {
if (swallowOutput) {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
if (getServletContext() != null) {
getServletContext().log(log);
} else {
out.println(log);
}
}
}
}
return servlet; }

  我们看到该方法是同步的(synchronized修饰符)。先看上面标黄的第一行,如果实现了之前说到的SigleThreadModel接口,那么这里的singleThreadModel就是true,就不会因为有Servlet实例而返回原有的Servlet了。但若反之,就返回原有的Servlet实例,符合单例的定义。

  第二处标黄说明Tomcat是通过反射来实例化Servlet的。它先根据web.xml(或者起相同作用的@WebServlet)找到ServletClass的全路径类名,然后通过类加载器得到Class对象,由Class对象取到构造器,通过构造器实例化ServletClass。

  最后看下例子:

package com.wlf.demo.servlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter; /**
* 一个servlet的demo
*/
@WebServlet(urlPatterns = {"/hello","/world"})
public class DemoServlet extends HttpServlet{ // 全局变量,多线程情况下将被改写
private String globalVariable = ""; public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // 后来的线程会覆盖前面的线程的值
globalVariable = request.getParameter("param"); // 给界面点击争取时间
try{
Thread.sleep(5000);
}catch (InterruptedException e)
{
e.printStackTrace();
} // 页面输出
PrintWriter pw = response.getWriter();
pw.println("<HTML>");
pw.println("<HEAD>");
pw.println("<title>Hello, world</title>");
pw.println("<body>");
pw.printf("<p>input: %s</p>", globalVariable);
pw.println("</HEAD>");
pw.println("</HTML>");
}
}

  我们启动tomcat,在浏览器打开两个页面分别访问,然后我先刷新hello页面,在它转圈圈时去刷新world页面,这时world的参数值就会覆盖hello的参数值:

  但如果我们给DemoServlet实现了SingleThreadModel:

public class DemoServlet extends HttpServlet implements SingleThreadModel

  重新编译、打包、部署、重启tomcat后,重试上面的例子,并发问题就不存在了。当然,既然官方不建议你使用单线程模式,那么我们还是别用了,毕竟性能比起多线程来就低很多了。而多线程的问题我们可以通过线程封闭(全局变量改为局部变量)和加同步锁(用到全局变量时增加synchronized代码块)等方法来解决。

Servlet是单例的吗?的更多相关文章

  1. struts2的action是多例,servlet是单例

    struts2中action是多例的,即一个session产生一个action如果是单例的话,若出现两个用户都修改一个对象的属性值,则会因为用户修改时间不同,两个用户访问得到的 属性不一样,操作得出的 ...

  2. Servlet以及单例设计模式

    1.Servlet概述 a)Servlet,全城是Servlet Applet,服务器端小程序,是一个接口,定义了若干方法,要求所有的Servlet必须实现. b)Servlet用于接收客户端的请求, ...

  3. Servlet是否单例?

    1,测试环境: Java SE版本:1.8.0_161(AMD64) Tomcat版本:9.0.7(AMD64) 2,试验 (1)编写HelloServlet. 由于测试代码很简单,此处只列出doGe ...

  4. struts2,servlet和springmvc的单例多例问题

    struts2,servlet和springmvc的单例多例问题 原创 2017年06月12日 09:59:21 标签: struts2 / servlet / springmvc / 单例 / 多例 ...

  5. Spring工作原理与单例

    最近看到spring管理的bean为单例的,当它与web容器整合的时候始终搞不太清除,就网上搜索写资料, Tomcat与多线程, servlet是多线程执行的,多线程是容器提供的能力. servlet ...

  6. SpringMVC中的Controller默认单例

    众所周知,Servlet是单例的. 在struts中,Action是多例的,每一个请求都会new出来一个action来处理. 在Spring中,Controller默认是单例的,多个请求都会访问同一个 ...

  7. 单例设计模式-java

    在实际项目中单例模式常见应用场景列举如下: 1.servlet编程中,每个servlet就是单例 2.网站计数器,和Application(servlet中涉及) 3.Strucs1框架中,控制器对象 ...

  8. Servlet 单例多线程

    Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在 ...

  9. servlet单例多线程

    Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在 ...

随机推荐

  1. PHP:第一章——PHP中的数组运算符和类运算符

    数组运算符: $a+$b;//$a和$b的联合 $a == $b;//比较$a与$b的值相同为true; $a === $b;//如果$a与$b的值与顺讯完全相同为true; $a !=$b;//如果 ...

  2. 利用express.js连接mongodb数据库

    var MongoClient = require('mongodb').MongoClient; var DB_CONN_STR = "mongodb://localhost:27017/ ...

  3. bzoj1599

    题解: 暴力枚举每一种可能性 代码: #include<bits/stdc++.h> using namespace std; int s1,s2,s3,ans,num; int main ...

  4. Mac OS X 10.9下解决cocos2d-x在Xcode4.6.x的模板不显示问题

    最近将iMac 升级到10.9了,奇怪的事情发生了,cocos2d-x的模板不见了,鼓捣了半天发现问题所在 打开xcode新建工程却找不到cocos2d-x的模板. 经过在网上的苦苦搜寻和试验后,找到 ...

  5. SaltStack自动化运维工具

    一.SaltStack的了解 SaltStack管理工具允许管理员对多个操作系统创建一个一致的管理系统,包括VMware vSphere环境. SaltStack作用于仆从和主拓扑.SaltStack ...

  6. Alpha阶段敏捷冲刺---Day3

    一.Daily Scrum Meeting照片 二.今天冲刺情况反馈 今天我们上完课后在禹洲楼教室外进行我们的每日立会.开会的内容主要是总结了一下这几天各自的任务及任务进度,交流了一下各自遇到的困难. ...

  7. apache的日志access_log分析

    正常日志格式:客户端地址 访问者的标识 访问者的验证名字 请求的时间 请求类型 请求的HTTP代码 发送给客户端的字节数   当网站出问题时分析日志,第一步一般都不会是看访问日志.但是也不能忽视它,在 ...

  8. Redis学习第五课:Redis Set类型及操作

    Set是集合,它是string类型的无序集合.set是通过hash table实现的,添加.删除和查找的复杂度都是O(1). 对集合我们可以取并集.交集.差集.通过这些操作我们可以实现SNS中的好友推 ...

  9. (libev) cannot allocate xxx bytes, aborting 问题的一种触发条件

    最近在项目中使用了libev,遇到一个很奇怪的crash,服务器运行随机时间后有一定的概率奔溃,输出的最后一行log是: (libev) cannot allocate xxx bytes, abor ...

  10. windows运行打开服务命令

    1. gpedit.msc-----组策略    2. sndrec32-------录音机   3. Nslookup-------IP地址侦测器   4. explorer-------打开资源管 ...