Servlet的線程安全
Servlet的線程安全
一、什麽是Servlet的線程安全
1.在Servlet的整個生命周期中,構造方法只被執行一次。也就是說,在Servlet的整個生命周期中,只存在一個Servlet實例對象。這說明Servlet是單例多線程的,可能會引起線程安全問題。
所謂線程安全就是一個Servlet實例對象會同時處理多個請求,這樣的Servlet工作效率的確很高。但如果Servlet中包含成員變量的話,可能一個線程對該成員變量進行寫操作,而另一個線程對該成員變量進行讀操作。所以,單例多線程的Servlet不能創建成員變量。
下面我將通過例子討論線程安全問題(不存在線程安全問題的代碼:)
1package gacl.servlet.study; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 /** 11 * 當多線程並發訪問這個方法裏面的代碼時,會存在線程安全問題嗎??12 * i變量被多個線程並發訪問,但是沒有線程安全問題,因為i是doGet方法裏面的局部變量, 13 * 當有多個線程並發訪問doGet方法時,每一個線程裏面都有自己的i變量, 14 * 各個線程操作的都是自己的i變量,所以不存在線程安全問題 15 * 多線程並發訪問某一個方法的時候,如果在方法內部定義了一些資源(變量,集合等) 16 * 那麽每一個線程都有這些東西,所以就不存在線程安全問題了 17 */ 18 public class ServletThread extends HttpServlet {19 20 21 public void doGet(HttpServletRequest request, HttpServletResponse response) 22 throws ServletException, IOException { 23 int a=1; 24 a++; 25 response.getWriter().write(a); 26 } 27 28 public void doPost(HttpServletRequest request, HttpServletResponse response) 29 throws ServletException, IOException { 30 doGet(request, response); 31 } 32 33 }
存在線程安全問題的代碼:
1 package gacl.servlet.study; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 public class ServletTreadDemo extends HttpServlet { 11 12 int i=1; 13 public void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 i++; 16 try { 17 Thread.sleep(1000*3); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 response.getWriter().write(i+""); 22 } 23 24 public void doPost(HttpServletRequest request, HttpServletResponse response) 25 throws ServletException, IOException { 26 doGet(request, response); 27 } 28 29 }
把i定義成全局變量,多個線程並發訪問變量全局變量 i 時,就會存在線程安全問題了,如果同時開啟兩個瀏覽器模擬並發訪問同一個Servlet,本來正常來說,第一個瀏覽器應該看到2,而第二個瀏覽器應該看到3的,結果兩個瀏覽器都看到了3。
2.線程安全問題的解決辦法:
1.加鎖(synchronized)
加了synchronized後,並發訪問i時就不存在線程安全問題了,為什麽加了synchronized後就沒有線程安全問題了呢?假如現在有一個線程訪問Servlet對象,那麽它就先拿到了Servlet對象的那把鎖等到它執行完之後才會把鎖還給Servlet對象,由於是它先拿到了Servlet對象的那把鎖,所以當有別的線程來訪問這個Servlet對象時,由於鎖已經被之前的線程拿走了,後面的線程只能排隊等候了。。這樣不科學。。如果有1000人同時訪問ServletThreadDemo1。後面的人還不等得得吐血!!
1 package gacl.servlet.study; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 11 public class ServletThreadDemo1 extends HttpServlet { 12 13 int i=1; 14 public void doGet(HttpServletRequest request, HttpServletResponse response) 15 throws ServletException, IOException { 16 synchronized (ServletThreadDemo1.class) {//在java中,每一個對象都有一把鎖,這裏的this指的就是Servlet對象 17 i++; 18 try { 19 Thread.sleep(5000*2); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 response.getWriter().println(i+""); 24 } 25 26 } 27 28 public void doPost(HttpServletRequest request, HttpServletResponse response) 29 throws ServletException, IOException { 30 doGet(request, response); 31 } 32 }
2.sun公司給的解決方案是:如果某個Servlet實現了SingleThreadModel接口,那麽Servlet引擎將以單線程模式來調用其service方法。 SingleThreadModel接口中沒有定義任何方法,只要在Servlet類的定義中增加實現SingleThreadModel接口的聲明即可。 對於實現了SingleThreadModel接口的Servlet,Servlet引擎仍然支持對該Servlet的多線程並發訪問,其采用的方式是產生多個Servlet實例對象,並發的每個線程分別調用一個獨立的Servlet實例對象。 但是實現SingleThreadModel接口並不能真正解決Servlet的線程安全問題,因為Servlet引擎會創建多個Servlet實例對象,而真正意義上解決多線程安全問題是指一個Servlet實例對象被多個線程同時調用的問題。事實上,在Servlet API 2.4中,已經將SingleThreadModel標記為Deprecated(過時的)。
Servlet的線程安全