zoukankan      html  css  js  c++  java
  • servlet是单例还是多例

    单实例多线程

    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;
    }

    那一个实现了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,如果声明多次,也会生成多个实例。

  • 相关阅读:
    每天一个linux命令(1):ls命令
    如何查看和停止Linux启动的服务
    JavaScript作用域原理——作用域根据函数划分
    iOS 自动布局详细介绍
    arc下内存泄漏的解决小技巧
    AFNetwork2.0在报错1016,3840的解决方法及一些感悟
    iOS聊天下拉刷新聊天记录的实现
    tableview直接滚动至最后一行
    UITabBar,UINavigationBar的布局和隐藏问题
    transformjs玩转星球
  • 原文地址:https://www.cnblogs.com/stono/p/14234241.html
Copyright © 2011-2022 走看看