1. 程式人生 > >servlet是否存線上程安全問題

servlet是否存線上程安全問題

        今天老師問了一句,servlet存線上程安全問題嗎,心想著servlet不是單例的嘛,每個執行緒在呼叫的時候都會為例項物件分配獨立的引用。我就以為servlet屬於執行緒安全的。晚上自己再網上查看了一下,發現servlet不是安全的。

        下面就給大家介紹一下servlet執行緒安全問題

        首先,預設的servlet是非執行緒安全的,servlet是單例模式,只產生一個例項,根據專案中web.xml例項,這個例項是web容器產生的,比如Tomcat,JBOSS,weblogic等。

        就是說多個客戶請求產生多個執行緒,一個執行緒對應一個客戶,但是用的servlet物件卻是一個。既然用的物件是一個,那麼例項變數就是共享資料了,說到共享資料還不同步,必然是非執行緒安全的。

        比如說兩人同時在12306上買票,這時很不巧只剩下一張票,甲乙在同一時間點選購票,兩人同時都到購票了,你們說到時候上車怎麼辦?

有這個例子可以看到servlet執行緒是不安全的,當對一個複雜物件進行某種操作時,從操作開始到操作結束,被操作的物件往往會經歷若干非法的中間狀態。呼叫一個函式(假設該函式是正確的)操作某物件常常會使該物件暫時陷入不可用的狀態(通常稱為不穩定狀態),等到操作完全結束,該物件才會重新回到完全可用的狀態。如果其他執行緒企圖訪問一個處於不可用狀態的物件,該物件將不能正確響應從而產生無法預料的結果,如何避免這種情況發生是執行緒安全性的核心問題。

這是我在網上找的例項,自己運行了一下顯示的結果,

public class threadSafe extends HttpServlet{
    private static final long serialVersionUID = 1L;
    private volatile int num = 0;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        addOne();
        response.getWriter().write("now access num : " 
+ getNum()); } /** * 讀取開銷低 */ private int getNum() { return num; } /** * 其寫入為非執行緒安全的,賦值操作開銷高 */ private synchronized void addOne() { num ++; } }

在兩個瀏覽器上不停地訪問這個地址,顯示的結果沒有出現多個執行緒共同的數字,



為什麼沒有出錯的,咱們在這裡加入了

synchronized關鍵字

synchronized關鍵字,代表這個方法加鎖,相當於不管哪一個執行緒(例如執行緒A),執行到這個方法時,都要檢查有沒有其它執行緒B(或者C、D等)正在用這個方法,若有則要等正在使用synchronized方法的執行緒B(或者C、D)執行完這個方法後再執行此執行緒A,若沒有則直接執行。

常見的執行緒安全的解決辦法: 

1.使用方法內區域性變數 
是因為各執行緒有自己堆疊空間,儲存區域性變數 
方法引數傳入,多采用傳值(volue copy)傳入方法內 

2.對操作共享資源的語句,方法,物件, 使用同步 
比如寫入磁碟檔案,採用同步鎖,但建議儘量用同步程式碼塊,不要用同步方法 


3.使用同步的集合類 
使用Vector代替ArrayList 
使用Hashtable代替HashMap。 

4.不要在 Servlet中再建立自己的執行緒來完成某個功能。 
Servlet本身就是多執行緒的,在Servlet中再建立執行緒,將導致執行情況複雜化