天天看點

淺析Struts1和Struts2的Action線程安全問題

這是由于servlet的工作原理産生的。我們先來簡單回顧一下servlet的生命周期“初始化->init->service->destroy->解除安裝”。

這裡大家都知道,我們在web.xml裡面定義一個servlet的時候,我們可以給他們設定一個“load-on-startup” 的值,如果 servlet 的 load-on-startup 配置項大于 0,那麼在 context 容器啟動的時候就會被執行個體化,并且tomcat給每一個servlet加載并且執行個體化一個對象(注解:也就是說,我們使用者在web.xml裡面配置的每一個servlet都會被執行個體成一個servlet對象)

a, 下面的配置表示會有兩個servlet對象被執行個體化,即使他們對應的是同一個servlet class

<?xml version="1.0" encoding="utf-8"?>  

<web-app version="2.5"   

    xmlns="http://java.sun.com/xml/ns/javaee"   

    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"   

    xsi:schemalocation="http://java.sun.com/xml/ns/javaee   

    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  

  <servlet>  

    <servlet-name>servlettest1</servlet-name>  

    <servlet-class>web.servlet.servlettest1</servlet-class>  

  </servlet>  

  <servlet-mapping>  

    <url-pattern>/servlet/servlettest1</url-pattern>  

  </servlet-mapping>  

    <servlet-name>servlettest2</servlet-name>  

</web-app>  

b, 下面的配置表示隻會有一個servlet被執行個體化

也就是說,tomcat容器對servlet的實作采用的是單例模式,對于一個servlet類,永遠隻有一個servlet對象存在。

下面我們來解釋為什麼struts1是線程不安全的。

1、struts1

struts1是對java web servlet接口的直接實作,是以它繼承了tomcat對servlet的實作,每一個struts1裡面的action都對應的是一個servlet class,是以這裡的action在被tomcat執行個體化之後也是單例的,是以,struts1就産生了多線程問題。

例如:

你在action定義了一個 int i = 0;

然後在這個action裡面的某一個方法裡面對這個i進行操作。

向下面代碼這樣:

package web.servlet;  

import java.io.ioexception;  

import java.io.printwriter;  

import javax.servlet.servletexception;  

import javax.servlet.http.httpservlet;  

import javax.servlet.http.httpservletrequest;  

import javax.servlet.http.httpservletresponse;  

/** 

 * @author jack zhang 

 * @version vb1.0 

 * @email [email protected] 

 * @date 2013-4-21 

 */  

public class servlettest1 extends httpservlet  

{  

    public int i = 0;  

    /** 

     * constructor of the object. 

     */  

    public servlettest1()  

    {  

        super();  

    }  

     * the doget method of the servlet. <br> 

     * 

     * this method is called when a form has its tag value method equals to get. 

     *  

     * @param request the request send by the client to the server 

     * @param response the response send by the server to the client 

     * @throws servletexception if an error occurred 

     * @throws ioexception if an error occurred 

    public void doget(httpservletrequest request, httpservletresponse response)  

        throws servletexception,  

        ioexception  

        i++;  

        response.setcontenttype("text/html");  

        printwriter out = response.getwriter();  

        out  

            .println("<!doctype html public \"-//w3c//dtd html 4.01 transitional//en\">");  

        out.println("<html>");  

        out.println("  <head><title>a servlet</title></head>");  

        out.println("  <body>");  

        out.print("    i="+i);  

        out.println("  </body>");  

        out.println("</html>");  

        out.flush();  

        out.close();  

}  

當通路這個servlet的時候,你通路多少次,i的值就是多少。

是以:我們在用struts1的時候不能在action裡面定義屬性。要用到隻的話隻能在方法裡面定義。

那至于為什麼把屬性的定義放到方法裡面就不會有多線程的問題了,這個問題希望各位讀者去檢視有關jmm(java memory model)裡面有關java記憶體模式如何給方法配置設定記憶體的内容, 我相信你們會找到答案。

2、struts2

上面我們了解了struts1裡面的多線程問題,那struts2是怎麼解決這個問題的呢?其實道理非常簡單,原因就是strtus2會擷取到使用者的http請求,然後負責給每個請求執行個體化一個action 對象,但是大家注意,這裡的action對象和struts1裡面的action對象完全不是一個概念,struts1裡面的action類就是一個servlet類,而這裡的action類隻是一個普通的java class。這也就是為什麼struts1裡面的action是線程不安全的,而struts2裡面的action是線程安全的原因。

那我們在回頭來看看struts2對servlet的處理和struts1有什麼不同。看過前面分析的讀者肯定知道,struts1的 action對servlet沒有進行任何的包裝,它是直接實作的java web api 裡面的servlet 接口。是以才會有線程安全的問題,但是struts2底層幫我們封裝了servlet,使開發人員不用直接接觸servlet。具體做法是:

strtus2截獲servlet請求,然後給每個請求執行個體化一個action對象,請求結束之後銷毀action對象。至于strtus2具體是怎麼做的,我這裡不贅叙,大家可以去參看struts2的有關介紹。

在struts2中由于 action和普通的java類沒有任何差別(也就是不用像struts1裡面那樣去實作一個struts的接口,有興趣的朋友可以自己去了解),是以我們可以用spring去管理struts2的action,這個時候我們就要注意了,因為當我們在spring裡面去定義bean的時候,spring預設用的是單例模式。是以在這個時候,你就要修改spring的配置檔案---即修改scope為prototype。