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對象,完成成員變量賦值。
發佈了73 篇原創文章 · 獲贊 13 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章