1. 程式人生 > >struts1 和struts2 執行緒安全

struts1 和struts2 執行緒安全

首先我們必須要先了解servlet的生命週期:

伺服器只建立每個servlet的單一例項,首次建立servlet時,它的init方法會被呼叫,因此,init是放置一次性設定程式碼的地方,之後,針對每個使用者的請求都會建立一個執行緒,該執行緒呼叫前面建立的例項方法。多個併發請求一般會導致多個執行緒同時呼叫service(執行緒安全),service方法會依據接受到HTTP請求的型別,呼叫doXXX方法。最後如果伺服器解除安裝某個servlet就會呼叫servlet的destroy方法。

servlet是否執行緒安全?

通常情況下,系統只生成servlet的單一例項之後,為每個使用者請求建立新的執行緒。如果很多請求同時到來,那麼多個執行緒可能會併發的訪問同一個servlet物件。因此我們必須小心地同步對欄位以及全域性變數和其它共享資料的訪問,因為多個執行緒可能同時對同一資料進行訪問。(多個執行緒並不共享區域性變數)。

解決方法:

原則上,可以讓servlet實現singleThreadModel介面,阻止多執行緒訪問

Public class Servlet1 extends HttpServlet implements SingleThreadModel{

………

}

實現此介面。大多數情況下,系統將所有的請求排隊,一次只將一個請求傳送給單個servlet例項。但是,伺服器也可以建立由多個例項組成的池,同一時間每個例項都能夠請求處理,此時我們不需要擔心隊servlet常規欄位(例項變數)的同時訪問,但是必須同步對類變數或儲存在servlet之外的共享資料的訪問,

大部分情況下,SingleThreadModel並不是一個很好的選項。如果servlet被頻繁訪問,那麼同步對servlet的訪問對效能造成極大的損害(等待時間)。在servlet等待I/O,資料庫訪問等其它操作時,伺服器不能處理同一servlet的掛起請求。

singleThreadModel的第二個問題來源於,Servlet規範允許伺服器使用多個例項來處理請求來替代對單個例項的請求進行排隊的方案。只要每個例項同一時間處理一個請求,例項共享的方式就滿足規範的要求,但這不是一個好的方案:

當使用非靜態例項變數儲存共享資料,singleThreadModel會阻止併發的訪問,但是每個servlet例項都擁有例項變數的單獨副本,資料就不能共享,

使用靜態例項變數儲存共享資料:singleThreadModel的共享池也就沒有任何優點,多個請求依舊會併發的訪問靜態資料。

對於高流量的servlet,使用明確的程式碼同步更好一些。

以一個簡單的例子說明,給出3種方案

String id = “user id =” + userId;

Do something….

userId = userId + 1;

1:減少競爭

Sting id = “user id =” + userId++;這種方案減低了產生不正確答案的可能性,但沒有完全消除,在很多情況下,減低錯誤出現的可能性是一場災難,問題在測試中更難以發現

2:SingleThreadModel

將servlet做如下修改

public class UserId extends HttpServlet implements SingleThreadModel{

如果伺服器是通過排隊所有的請求來實現SingleThreadModel,那它能工作,但如果村子大量的併發訪問,這種方式會導致效能上的巨大損失。

如果伺服器通過產生servlet的多個例項來實現SingleThreadModel。這種方式會完全失敗,因為每個例項都擁有自己的userId欄位。

3:使用同步

Synchronized(this){

   String id = “user id =” + userId;

Do something….

userId = userId + 1;

}

Struts 1 和 Struts 2 的執行緒安全
在最近使用struts2的時候,都會看到別人寫的action中會加上一個scope(“prototype”)
這很是奇怪。為什麼要加這個。。
在struts1中並不會加這個。。而且對於struts1中的action都是單例的。雖然聽說Singleton是個反模式,聽說有效能問題。但在使用struts1的時候沒有去深究,因為大家都這麼用,用得很自然。

在查詢資料的時候又看到了很多沒有加上scope的問題
http://blog.csdn.net/foamflower/archive/2009/07/08/4329989.aspx
http://elf8848.javaeye.com/blog/356746
這些都是因為沒有加上scope,導致上一次的變數值影響了下一次的請求。
還有如下問題:也讓我想不通。
引用
《strut2權威指南》中有這麼一段:
執行緒模式方面的對比:Struts1 Action是單例模式並且必須是執行緒安全的,因為僅有Action的一個例項來處理所有的請求。單例策略限制了Struts1 Action能做的事,並且要在開發時特別小心。Action資源必須是執行緒安全的或同步的;Struts 2 Action物件為每一個請求產生一個例項,因此沒有執行緒安全問題 。

由於對設計模式不太瞭解,做了這麼之年Struts,一直不知道struts1是單例模式,不知這一點是從哪體現出來的?
而在開發過程中要怎樣注意“執行緒安全或同步”?struts2從哪體現的“沒有執行緒安全問題”?

那麼執行緒的問題到底體現在哪兒呢?

在查了些資料後。。明白了些!
http://hi.baidu.com/platon/blog/item/64a20ff3f96e7fce0b46e031.html
這文章中講了最基本的servlet的多執行緒問題。
其實servlet本身就是個多執行緒環境下的單例類。web容器就是這樣來處理servlet的。

通過這文章知道,為什麼struts1中並沒有考慮到執行緒問題,因為所有的程式碼都是寫在execute的方法中,所有變數都是定義在裡面,所以沒有執行緒安全問題。

而現在的struts2就不一樣了。struts2的action中就像一個POJO一樣,定義了很多的類變數。這就有執行緒安全問題了。。此時,就使用scope=prototype來指定是個原型模式,而不是單例,這樣就解決了執行緒安全問題。每個執行緒都是一個新的例項。。

http://hi.baidu.com/niujunkai/blog/item/021964adc130660a4a36d6ab.html
這文章也說明了問題的關鍵之處。
引用
但是,執行緒同步是不得以的方法,是比較複雜的,而且會帶來效能的損失。等效的程式碼中,不需要同步在編寫容易度和效能上會更好些。
我這裡強調的是什麼程式碼是始終為執行緒安全的、是不需要同步的。如下:
1)常量始終是執行緒安全的,因為只存在讀操作。
2)對構造器的訪問(new 操作)是執行緒安全的,因為每次都新建一個例項,不會訪問共享的資源。
3)最重要的是:區域性變數是執行緒安全的。因為每執行一個方法,都會在獨立的空間建立區域性變數,它不是共享的資源。區域性變數包括方法的引數變數。
struts user guide裡有:
Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class.
譯:只使用用區域性變數。–編寫執行緒安全的程式碼最重要的原則就是,在Action類中只使用區域性變數,不使用例項變數。

struts2本身就是多例項單執行緒的,所以可以說 本身就是執行緒安全的。
struts1則是單例項多執行緒的,所以必須要注意執行緒的安全性。
所以相對來說,struts1 要多使用區域性變數,而struts2使用 例項變數則不會產生不安全的結果