Servlet/JSP技術和ASP
PHP等相比
由於其多線程運行而具有很高的執行效率
由於Servlet/JSP默認是以多線程模式執行的
所以
在編寫代碼時需要非常細致地考慮多線程的安全性問題
然而
很多人編寫Servlet/JSP程序時並沒有注意到多線程安全性的問題
這往往造成編寫的程序在少量用戶訪問時沒有任何問題
而在並發用戶上升到一定值時
就會經常出現一些莫明其妙的問題
Servlet的多線程機制 Servlet體系結構是建立在Java多線程機制之上的
它的生命周期是由Web容器負責的
當客戶端第一次請求某個Servlet時
Servlet容器將會根據web
xml配置文件實例化這個Servlet類
當有新的客戶端請求該Servlet時
一般不會再實例化該Servlet類
也就是有多個線程在使用這個實例
Servlet容器會自動使用線程池等技術來支持系統的運行
如圖
所示
圖 Servlet線程池 這樣
當兩個或多個線程同時訪問同一個Servlet時
可能會發生多個線程同時訪問同一資源的情況
數據可能會變得不一致
所以在用Servlet構建的Web應用時如果不注意線程安全的問題
會使所寫的Servlet程序有難以發現的錯誤
Servlet的線程安全問題 Servlet的線程安全問題主要是由於實例變量使用不當而引起的
這裡以一個現實的例子來說明
Import javax
servlet
*;
Import javax
servlet
http
*;
Import java
io
*;
Public class Concurrent Test extends HttpServlet {PrintWriter output;
Public void service (HttpServletRequest request
HttpServletResponse response) throws ServletException
IOException {String username;
Response
setContentType (
text/html; charset=gb
);
Username = request
getParameter (
username
);
Output = response
getWriter ();
Try {Thread
sleep (
); //為了突出並發問題
在這設置一個延時
} Catch (Interrupted Exception e){}
output
println(
用戶名:
+Username+
<BR>
);
}
}
該Servlet中定義了一個實例變量output
在service方法將其賦值為用戶的輸出
當一個用戶訪問該Servlet時
程序會正常的運行
但當多個用戶並發訪問時
就可能會出現其它用戶的信息顯示在另外一些用戶的浏覽器上的問題
這是一個嚴重的問題
為了突出並發問題
便於測試
觀察
我們在回顯用戶信息時執行了一個延時的操作
假設已在web
xml配置文件中注冊了該Servlet
現有兩個用戶a和b同時訪問該Servlet(可以啟動兩個IE浏覽器
或者在兩台機器上同時訪問)
即同時在浏覽器中輸入
a
//localhost:
/servlet/ConcurrentTest? Username=a
b
//localhost:
/servlet/ConcurrentTest? Username=b
如果用戶b比用戶a回車的時間稍慢一點
將得到如圖
所示的輸出
educitycn/img_///jpg> 圖 a用戶和b用戶的浏覽器輸出 從圖
中可以看到
Web服務器啟動了兩個線程分別處理來自用戶a和用戶b的請求
但是在用戶a的浏覽器上卻得到一個空白的屏幕
用戶a的信息顯示在用戶b的浏覽器上
該Servlet存在線程不安全問題
下面我們就從分析該實例的內存模型入手
觀察不同時刻實例變量output的值來分析使該Servlet線程不安全的原因
Java的內存模型JMM(Java Memory Model)JMM主要是為了規定了線程和內存之間的一些關系
根據JMM的設計
系統存在一個主內存(Main Memory)
Java中所有實例變量都儲存在主存中
對於所有線程都是共享的
每條線程都有自己的工作內存(Working Memory)
工作內存由緩存和堆棧兩部分組成
緩存中保存的是主存中變量的拷貝
緩存可能並不總和主存同步
也就是緩存中變量的修改可能沒有立刻寫到主存中
堆棧中保存的是線程的局部變量
線程之間無法相互直接訪問堆棧中的變量
根據JMM
我們可以將論文中所討論的Servlet實例的內存模型抽象為圖
所示的模型
educitycn/img_///jpg> 圖 Servlet實例的JMM模型 下面根據圖
所示的內存模型
來分析當用戶a和b的線程(簡稱為a線程
b線程)並發執行時
Servlet實例中所涉及變量的變化情況及線程的執行情況
如圖
所示
educitycn/img_///gif> 圖 Servlet實例的線程調度情況 從圖
中可以清楚的看到
由於b線程對實例變量output的修改覆蓋了a線程對實例變量output的修改
從而導致了用戶a的信息顯示在了用戶b的浏覽器上
如果在a線程執行輸出語句時
b線程對output的修改還沒有刷新到主存
那麼將不會出現圖
所示的輸出結果
因此這只是一種偶然現象
但這更增加了程序潛在的危險性
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27302.html