servlet是線程安全的嗎?
這個問題,在網上沒有看到一個確切的答案,所以我們來分析一下:
首先什么是線程安全?
引用概念:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。
那么我們都知道servlet是多線程的,同時一個servlet實現類只會有一個實例對象,也就是它是Singleton的,所以多個線程是可能會訪問同一個servlet實例對象的。
每個線程都會為數據實例對象開辟單獨的引用,那么servlet會是線程安全的嗎?
要判斷是否是線程安全,我們需要知道線程安全問題是由什么引起的。
搜索得到答案:線程安全問題都是由全局變量及靜態變量引起的。
看到這個答案,突然想起很多年前調查過的一個bug, 那時我們系統中遺留的代碼中寫了很多全局變量,有一次發布后,客戶反饋,當有多人同時進行某個操作時,我們的數據出了問題,那時我們調查后的結果就是:多人同步操作時,有些全局變量的值不對了,之后我們專門設一個人花了很多工夫來將所有全局變量都改成了局部變量了,並且項目要求以后不允許用全局變量。原來那時侯我就已經碰到過線程不安全的情況了啊,不過處理方式或者不用全局,或者加入同步,若加入同步同時也要考慮一下對程序效率會不會產生影響。
由此可知,servlet是否線程安全是由它的實現來決定的,如果它內部的屬性或方法會被多個線程改變,它就是線程不安全的,反之,就是線程安全的。
在網上找到一個例子,如下:
public class TestServlet extends HttpServlet {
private int count = 0;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().println("<HTML><BODY>");
response.getWriter().println(this + " ==> ");
response.getWriter().println(Thread.currentThread() + ": <br>");
for(int i=0;i<5;i++){
response.getWriter().println("count = " + count + "<BR>");
try {
Thread.sleep(1000);
count++;
} catch (Exception e) {
e.printStackTrace();
}
}
response.getWriter().println("</BODY></HTML>");
}
}
當同時打開多個瀏覽器,輸入http://localhost:8080/ServletTest/TestServlet時,他們顯示的結果不同,這就說明了對於屬性count來說,它是線程不安全的,
為了解決這個問題,將代碼重構,如下:
public class TestServlet extends HttpServlet {
private int count = 0;
private String synchronizeStr = "";
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().println("<HTML><BODY>");
response.getWriter().println(this + " ==> ");
response.getWriter().println(Thread.currentThread() + ": <br>");
synchronized (synchronizeStr){
for(int i=0;i<5;i++){
response.getWriter().println("count = " + count + "<BR>");
try {
Thread.sleep(1000);
count++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
response.getWriter().println("</BODY></HTML>");
}
}
