|
|
基本上Servlet容器會為每一個Servlet註冊名稱實例化一個物件,來自用戶端的請求會以一個執行緒來存取這個物件,如果有多個請求同時到達,就會有多個執行緒存取同一個Servlet實例,而這所引發的就是執行緒的安全問題。 如果您的Servlet實例之方法都是使用區域變數,這並不會有太大的問題,每一個執行緒對方法的呼叫會使用自己的區域變數,然而如果您的 Servlet方法中有共用一些field成員或是靜態成員,在多個執行緒同時存取時,就會有執行緒安全問題,例如一個簡單的計數器問題:
package onlyfun.caterpillar; 在上面這個Servlet中,由於count是field成員,在Servlet載入後,只有一個Servlet實例,每個請求以一個執行緒來存取這個實 例,有可能發生在前一個執行緒正在執行out.println(),而後一個執行緒正好執行count++,這會導致 count的計數不連續的情況發生,這個情況在這個例子中並不致於引發多大的問題,但它所代表的是多執行緒存取同一個Servlet的執行緒安全問題。 要求執行緒安全最基本的,就是對共用資源作同步化,例如對doGet()使用synchronized關鍵字: public synchronized void doGet(HttpServletRequest req,
HttpServletResponse res) 或者是: ....
PrintWriter out = res.getWriter(); synchronized(this) { count++; out.println("This servlet is accessd " + count + "times"); } .... 同步化所帶來的是延遲,當同步化的區塊被鎖定時,其他的執行緒必須等待鎖定的解除,這所代表的就是對使用者請求的延遲,在伺服器上這點延遲在用戶端多時就 會導致使用者必須花費長時間來等待回應,可以將同步化區塊儘量縮小來減少延遲,或者是完全使用區域變數,使得執行緒之間不共用某些資源,然後不共用資源所 帶來的,就是某些資訊將無法實現持續性,為了實現持續性可能必須額外花費一些手續。 回顧一下JSP中在<%! 與 %>之間宣告的變數,在生成Servlet之後,就是生成一個field成員,所以在<%! 與 %>之間宣告變數,也必須小心執行緒安全問題,通常並不建議這麽宣告變數,而是在<% 與 %>之間宣告變數,因為在轉為Servlet之間,它是_jspService()中的一個區域變數,即使在多個用戶端執行緒同時存取時,也不致於 發生執行緒共用資源的問題。 |