(转)聊聊Servlet、Struts一、Struts2以及SpringMvc中的线程安全

前言

不少初学者,甚至是工做1-3年的小伙伴们均可能弄不明白?servlet Struts1 Struts2 springmvc 哪些是单例,哪些是多例,哪些是线程安全?html

在谈这个话题以前,咱们先了解一下Java中相关的变量类型以及内存模型JMM。web

变量类型

  • 类变量:独立于方法以外的变量,用 static 修饰。
  • 局部变量:类的方法中的变量。
  • 实例变量(全局变量):独立于方法以外的变量,不过没有 static 修饰。

JAVA的局部变量

  • 局部变量声明在方法、构造方法或者语句块中;
  • 局部变量在方法、构造方法、或者语句块被执行的时候建立,当它们执行完成后,变量将会被销毁;
  • 访问修饰符不能用于局部变量;
  • 局部变量只在声明它的方法、构造方法或者语句块中可见;
  • 局部变量是在栈上分配的。
  • 局部变量没有默认值,因此局部变量被声明后,必须通过初始化,才可使用。

JAVA的实例变量

  • 实例变量声明在一个类中,但在方法、构造方法和语句块以外;
  • 当一个对象被实例化以后,每一个实例变量的值就跟着肯定;
  • 实例变量在对象建立的时候建立,在对象被销毁的时候销毁;
  • 实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部可以经过这些方式获取实例变量信息;
  • 实例变量能够声明在使用前或者使用后;
  • 访问修饰符能够修饰实例变量;
  • 实例变量对于类中的方法、构造方法或者语句块是可见的。通常状况下应该把实例变量设为私有。经过使用访问修饰符可使实例变量对子类可见;
  • 实例变量具备默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值能够在声明时指定,也能够在构造方法中指定;实例变量能够直接经过变量名访问。但在静态方法以及其余类中,就应该使用彻底限定名:ObejectReference.VariableName。

JAVA的类变量(静态变量)

  • 类变量也称为静态变量,在类中以static关键字声明,但必须在方法构造方法和语句块以外。
  • 不管一个类建立了多少个对象,类只拥有类变量的一份拷贝。
  • 静态变量除了被声明为常量外不多使用。常量是指声明为public/private,final和static类型的变量。常量初始化后不可改变。
  • 静态变量储存在静态存储区。常常被声明为常量,不多单独使用static声明变量。
  • 静态变量在程序开始时建立,在程序结束时销毁。
  • 与实例变量具备类似的可见性。但为了对类的使用者可见,大多数静态变量声明为public类型。
  • 默认值和实例变量类似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。变量的值能够在声明的时候指定,也能够在构造方法中指定。此外,静态变量还能够在静态语句块中初始化。
  • 静态变量能够经过:ClassName.VariableName的方式访问。
  • 类变量被声明为public static final类型时,类变量名称通常建议使用大写字母。若是静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致。

Java的内存模型JMM

Java的内存模型JMM(Java Memory Model)JMM主要是为了规定了线程和内存之间的一些关系。根据JMM的设计,系统存在一个主内存(Main Memory),Java中全部实例变量都储存在主存中,对于全部线程都是共享的。每条线程都有本身的工做内存(Working Memory),工做内存由缓存和堆栈两部分组成,缓存中保存的是主存中变量的拷贝,缓存可能并不总和主存同步,也就是缓存中变量的修改可能没有马上写到主存中;堆栈中保存的是线程的局部变量,线程之间没法相互直接访问堆栈中的变量。根据JMM,咱们能够将论文中所讨论的Servlet实例的内存模型抽象为下图所示的模型。spring

3.png

3.png缓存

 

线程安全

Servlet

Servlet/JSP技术和ASP、PHP等相比,因为其多线程运行而具备很高的执行效率。因为Servlet/JSP默认是以多线程模式执行的,因此,在编写代码时须要很是细致地考虑多线程的安全性问题。然而,不少人编写Servlet/JSP程序时并无注意到多线程安全性的问题,这每每形成编写的程序在少许用户访问时没有任何问题,而在并发用户上升到必定值时,就会常常出现一些莫明其妙的问题。安全

Servlet的多线程机制

Servlet体系结构是创建在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet 时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,通常不会再实例化该 Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行,以下图所示。多线程

1.png

1.png并发

 

这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的状况,数据可能会变得不一致。因此在用Servlet构建的Web应用时若是不注意线程安全的问题,会使所写的Servlet程序有难以发现的错误。mvc

Servlet的线程安全问题

Servlet的线程安全问题主要是因为实例变量使用不当而引发的,这里以一个现实的例子来讲明。post

/**
 * 模拟用户AB在同时执行不一样的动做
 * 先执行 http://localhost:8080/concurrent?username=A&action=play
 * 稍后执行 http://localhost:8080/concurrent?username=B&action=eat
 */
public class Concurrent extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private String action = "";//动做  
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {  
            String username = request.getParameter("username");  
            action = request.getParameter("username");  
            Thread.sleep(5000); //为了突出并发问题,在这设置一个延时  
            //若是不出意外,应该用户AB都在吃饭
            System.out.println("用户:"+username+"在"+action);
        } catch (Exception e) {  
        }  
    }
}

 

Struts1

首先,明确一点Sturts1 action是单例模式,线程是不安全的。Struts1使用的ActionServlet是单例的,既然是单例,当使用实例变量的时候就会有线程安全的问题。全部通常在开发中试禁止使用实例变量的。性能

Struts2

struts2使用的是actionContext,都是使用里面的实例变量,让struts2自动匹配成对象的。每次处理一个请求,struts2就会实例化一个对象,这样就不会有线程安全的问题了。

须要注意的是,若是struts2+spring来管理注入的时候,不要把Action设置成单例,不然会出问题的。固然如今不多有项目使用struts2了。

SpringMVC

SpringMVC的controller默认是单例模式的,因此也会有多线程并发的问题。

总结

  • servlet Struts1 SpringMvc 是线程不安全的,固然若是你不使用实例变量也就不存在线程安全的问题了。
  • Struts2 是线程安全的,固然前提状况是,Action 不交给 spring管理,而且不设置为单例。
  • SpringMvc 的 Bean 能够设置成多例变成线程安全,可是必定程度上回影响系统性能。
qrcode_for_gh_bf7a27ade681_258.jpg

做者: 小柒

出处: https://blog.52itstyle.vip

分享是快乐的,也见证了我的成长历程,文章大多都是工做经验总结以及平时学习积累,基于自身认知不足之处在所不免,也请你们指正,共同进步。

本文版权归做者全部,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出, 若有问题, 可邮件(345849402@qq.com)咨询。

相关文章
相关标签/搜索