Servlet/JSP、Struts1、Struts2以及SpringMVC的线程安全性

Servlet/JSP、Struts1、Struts2以及SpringMVC的线程安全性


    一、Servlet/JSP

    Servlet/JSP一直都是MVC界的老大哥,我们来回顾一下Servlet的生命周期。

    当客户端第一次请求Servlet时,Web容器会根据web.xml中的配置文件创建一个Servlet实例,而后调用init()方法,仅一次(注意);之后每一次请求都会执行Servlet实例中的service()方法;最后在容器销毁时,调用destroy()方法。

    我们大可以通过以上内容推测Servlet处理请求的方式:单实例、多线程。我们来看看下图,了解一下是什么意思:


    当客户端第一次发出对这个Servlet的请求时,Tomcat会新建这个Servlet的实例,此后就不会再去新建Servlet实例,这也能够解释,为什么init()方法只执行一次了。

    而当有多次的客户端请求时,Tomcat会分配一个新的线程去处理这次请求,操作同一个Servlet实例。

    好了线程安全问题由此引发,我们编写如下一段代码:

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        String username = request.getParameter("username");
        action = request.getParameter("action");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(username + "正在" +action);
    }

    代码比较简单,为一个doGet请求方法,获取username和action两个参数,其中action为Servlet的成员变量,username为局部变量,为了更加容易地去制造并发问题,我们加了一个Thread.sleep(),最后打印结果。我们用以下两组URL,先后输入在浏览器上,测试以上代码:



    得到如下的结果:


    很显然,也很完美的一个非线程安全事故。具体原因在于:局部变量是在每一个线程的栈的栈帧上分配的,而对象的内存是在堆上分配,在A“eat”的时候,B擅自将action改为了“fly”,导致A在休眠五秒后发现自己不是在吃东西,而是在飞。

    所以,如果在使用Servlet构建web项目时,如果不考虑线程安全,很容易发生隐患。

    二、Struts1

    Struts1使用的是ActionServlet,同样也是单例的。所以为了在开发过程中避免线程安全问题,会明确拒绝使用成员变量。

    三、Struts2

    Struts2是一个很老旧的MVC框架了,网上已经说基本上项目不会再去使用Struts2了,但是于2018年5月末,鄙人公司依旧在使用着Struts2。。。Struts2使用的actionContext,在使用中传参大多依赖于成员变量,自动给成员变量赋值,所以为了避免线程安全问题,Struts2是多例的。

    但是当与Spring整合时需要注意,必须避免将其Action设置为单例的,否则会发生线程安全问题。

    四、SpringMVC

    作为现在主流的MVC框架,SpringMVC还是拥有着强大的生命力的,他的Controller类是和Spring整合的,所以如果不去修改Bean的单例模式,Controller类就是单例的。但是他的传参不依赖于成员变量,所以,当没有在Controller类中使用成员变量,就不会有线程安全问题。

    五、说说SpringMVC和Struts2的区别和优缺点

  1. 实现机制
    SpringMVC使用的是Servlet实现中央控制器,Struts2使用的是Filter实现的请求拦截。就Servlet和Filter的区别来说,Servlet是在第一次请求时实例,Filter是在容器加载时便实例化。
  2. 拦截机制
    Struts2的拦截是类级别的,对每一个Request的传参,通过调用成员变量的getter和setter方法赋值,所以对每一个Action类都生成一个实例。
    SpringMVC的拦截是方法级别的,一个URL对应一个方法,传入参数直接注入给方法的局部变量,包括它的request和response对象也是方法中的局部变量,所以也不存在线程安全问题。
  3. 性能方面
    由于单例多例的原因,SpringMVC性能肯定是优于Struts2的,因为Struts2每次请求都需要生成一个Action对象,完成成员变量赋值。

猜你喜欢

转载自blog.csdn.net/that_is_cool/article/details/80498940