Java servlet多線程

Servlet體系結構是創建在Java多線程機制之上的,它的生命週期是由Web容器負責的。當客戶端第一次請求某個Servlet 時,Servlet容器將會根據web.xml配置文件實例化這個Servlet類。當有新的客戶端請求該Servlet時,通常不會再實例化該 Servlet類,也就是有多個線程在使用這個實例。 這樣,當兩個或多個線程同時訪問同一個Servlet時,可能會發生多個線程同時訪問同一資源的狀況,數據可能會變得不一致。因此在用Servlet構建的Web應用時若是不注意線程安全的問題,會使所寫的Servlet程序有難以發現的錯誤。 html

  實例變量不正確的使用是形成Servlet線程不安全的主要緣由。下面針對該問題給出了三種解決方案並對方案的選取給出了一些參考性的建議。 java

一、實現 SingleThreadModel 接口(不推薦,下文會具體講弊端) web

  該接口指定了系統如何處理對同一個Servlet的調用。若是一個Servlet被這個接口指定,那麼在這個Servlet中的service方法將不會有兩個線程被同時執行,固然也就不存在線程安全的問題。這種方法只要將前面的Concurrent Test類的類頭定義更改成: 瀏覽器

  Public class Concurrent Test extends HttpServlet implements SingleThreadModel { 緩存

  ………… 安全

  } 服務器

  二、同步對共享數據的操做 多線程

  使用synchronized 關鍵字能保證一次只有一個線程能夠訪問被保護的區段,在本論文中的Servlet能夠經過同步塊操做來保證線程的安全。同步後的代碼以下: 併發

  ………… 異步

  Public class Concurrent Test extends HttpServlet { …………

  Username = request.getParameter ("username");

  Synchronized (this){

  Output = response.getWriter ();

  Try {

  Thread. Sleep (5000);

  } Catch (Interrupted Exception e){}

  output.println("用戶名:"+Username+"

  ");

  }

  }

  }

  三、避免使用實例變量

  本實例中的線程安全問題是由實例變量形成的,只要在Servlet裏面的任何方法裏面都不使用實例變量,那麼該Servlet就是線程安全的。(實例變量在上段代碼裏能夠被Servlet裏的全部方法使用,故引發線程安全問題)

  修正上面的Servlet代碼,將實例變量改成局部變量實現一樣的功能,代碼以下:

  ……

  Public class Concurrent Test extends HttpServlet {public void service (HttpServletRequest request, HttpServletResponse

  Response) throws ServletException, IOException {

  Print Writer output;

  String username;

  Response.setContentType ("text/html; charset=gb2312");

  ……

  }

  }

  對上面的三種方法進行測試,能夠代表用它們都能設計出線程安全的Servlet程序。可是,若是一個Servlet實現了 SingleThreadModel接口,Servlet引擎將爲每一個新的請求建立一個單獨的Servlet實例,這將引發大量的系統開銷。 SingleThreadModel在Servlet2.4中已再也不提倡使用;一樣若是在程序中使用同步來保護要使用的共享的數據,也會使系統的性能大大降低。這是由於被同步的代碼塊在同一時刻只能有一個線程執行它,使得其同時處理客戶請求的吞吐量下降,並且不少客戶處於阻塞狀態。另外爲保證主存內容和線程的工做內存中的數據的一致性,要頻繁地刷新緩存,這也會大大地影響系統的性能。因此在實際的開發中也應避免或最小化 Servlet 中的同步代碼;在Serlet中避免使用實例變量是保證Servlet線程安全的最佳選擇。從Java 內存模型也能夠知道,方法中的臨時變量是在棧上分配空間,並且每一個線程都有本身私有的棧空間,因此它們不會影響線程的安全。

-------------------------------------------------------------------------------

  補充:

  servlet存在的多線程問題

  實例變量: 實例變量是在堆中分配的,並被屬於該實例的全部線程共享,因此不是線程安全的.

  JSP系統提供的8個類變量:

  JSP中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT是線程安全的,APPLICATION在整個系統內被使用,因此不是線程安全的.

  局部變量: 局部變量在堆棧中分配,由於每一個線程都有它本身的堆棧空間,因此是線程安全的.

  靜態類: 靜態類不用被實例化,就可直接使用,也不是線程安全的.

  外部資源: 在程序中可能會有多個線程或進程同時操做同一個資源(如:多個線程或進程同時對一個文件進行寫操做).

  此時也要注意同步問題. 使它以單線程方式執行,這時,仍然只有一個實例,全部客戶端的請求以串行方式執行。這樣會下降系統的性能

  對於存在線程不安全的類,如何避免出現線程安全問題:

  一、採用synchronized同步。缺點就是存在堵塞問題。

二、使用ThreadLocal(實際上就是一個HashMap),這樣不一樣的線程維護本身的對象,線程之間相互不干擾。 

 

一,servlet容器如何同時處理多個請求。

  Servlet採用多線程來處理多個請求同時訪問,Servelet容器維護了一個線程池來服務請求。

  線程池其實是等待執行代碼的一組線程叫作工做者線程(Worker Thread),Servlet容器使用一個調度線程來管理工做者線程(Dispatcher Thread)。

  當容器收到一個訪問Servlet的請求,調度者線程從線程池中選出一個工做者線程,將請求傳遞給該線程,而後由該線程來執行Servlet的service方法。

  當這個線程正在執行的時候,容器收到另一個請求,調度者線程將從池中選出另一個工做者線程來服務新的請求,容器並不關係這個請求是否訪問的是同一個Servlet仍是另一個Servlet。

  當容器同時收到對同一Servlet的多個請求,那這個Servlet的service方法將在多線程中併發的執行。

  二,Servlet容器默認採用單實例多線程的方式來處理請求,這樣減小產生Servlet實例的開銷,提高了對請求的響應時間。對於Tomcat能夠在server.xml中經過<Connector>元素設置線程池中線程的數目。

  就實現來講:

  調度者線程類所擔負的責任如其名字,該類的責任是調度線程,只須要利用本身的屬性完成本身的責任。因此該類是承擔了責任的,而且該類的責任又集中到惟一的單體對象中。

  而其餘對象又依賴於該特定對象所承擔的責任,咱們就須要獲得該特定對象。那該類就是一個單例模式的實現了。

  三,如何開發線程安全的 Servlet

  1,變量的線程安全:這裏的變量指字段和共享數據(如表單參數值)。

  a,將 參數變量 本地化。多線程並不共享局部變量.因此咱們要儘量的在servlet中使用局部變量。

  例如:String user = "";

  user = request.getParameter("user");

  b,使用同步塊Synchronized,防止可能異步調用的代碼塊。這意味着線程須要排隊處理。

  在使用同板塊的時候要儘量的縮小同步代碼的範圍,不要直接在sevice方法和響應方法上使用同步,這樣會嚴重影響性能。

  2,屬性的線程安全:ServletContext,HttpSession,ServletRequest對象中屬性

  ServletContext:(線程是不安全的)

  ServletContext是能夠多線程同時讀/寫屬性的,線程是不安全的。要對屬性的讀寫進行同步處理或者進行深度Clone()。

  因此在Servlet上下文中儘量少許保存會被修改(寫)的數據,能夠採起其餘方式在多個Servlet中共享,比方咱們可使用單例模式來處理共享數據。

  HttpSession:(線程是不安全的)

  HttpSession對象在用戶會話期間存在,只能在處理屬於同一個Session的請求的線程中被訪問,所以Session對象的屬性訪問理論上是線程安全的。

  當用戶打開多個同屬於一個進程的瀏覽器窗口,在這些窗口的訪問屬於同一個Session,會出現屢次請求,須要多個工做線程來處理請求,可能形成同時多線程讀寫屬性。

  這時咱們須要對屬性的讀寫進行同步處理:使用同步塊Synchronized和使用讀/寫器來解決。

  ServletRequest:(線程是安全的)

  對於每個請求,由一個工做線程來執行,都會建立有一個新的ServletRequest對象,因此ServletRequest對象只能在一個線程中被訪問。ServletRequest是線程安全的。

  注意:ServletRequest對象在service方法的範圍內是有效的,不要試圖在service方法結束後仍然保存請求對象的引用。

  3,使用同步的集合類:

  使用Vector代替ArrayList,使用Hashtable代替HashMap。

  4,不要在Servlet中建立本身的線程來完成某個功能。

  Servlet自己就是多線程的,在Servlet中再建立線程,將致使執行狀況複雜化,出現多線程安全問題。

  5,在多個servlet中對外部對象(比方文件)進行修改操做必定要加鎖,作到互斥的訪問。

  四,SingleThreadModel接口

  javax.servlet.SingleThreadModel接口是一個標識接口,若是一個Servlet實現了這個接口,那Servlet容器將保證在一個時刻僅有一個線程能夠在給定的servlet實例的service方法中執行。將其餘全部請求進行排隊。

  服務器可使用多個實例來處理請求,代替單個實例的請求排隊帶來的效益問題。服務器建立一個Servlet類的多個Servlet實例組成的實例池,對於每一個請求分配Servlet實例進行響應處理,以後放回到實例池中等待下此請求。這樣就形成併發訪問的問題。

  此時,局部變量(字段)也是安全的,但對於全局變量和共享數據是不安全的,須要進行同步處理。而對於這樣多實例的狀況SingleThreadModel接口並不能解決併發訪問問題。

  SingleThreadModel接口在servlet規範中已經被廢棄了。

相關文章
相關標籤/搜索