servlet多线程
? ? ?Servlet是單實例多線程,servlet只會在容器啟動的時候初始化一次,以后web容器的每同一個請求都不會在創建servlet的實例。
一、Servlet采用多線程來處理多個請求同時訪問,Servelet容器維護了一個線程池來服務請求。
? ? ?線程池實際上是等待執行代碼的一組線程叫做工作者線程(Worker Thread),Servlet容器使用一個調度線程來管理工作者線程(Dispatcher Thread)。
? ? ?當容器收到一個訪問Servlet的請求,調度者線程從線程池中選出一個工作者線程,將請求傳遞給該線程,然后由該線程來執行Servlet的service方法。
? ? ?當這個線程正在執行的時候,容器收到另外一個請求,調度者線程將從池中選出另外一個工作者線程來服務新的請求,容器并不關系這個請求是否訪問的是同一個Servlet還是另外一個Servlet。
? ? ?當容器同時收到對同一Servlet的多個請求,那這個Servlet的service方法將在多線程中并發的執行。
?
二、Servlet容器默認采用單實例多線程的方式來處理請求,這樣減少產生Servlet實例的開銷,提升了對請求的響應時間。對于Tomcat可以在server.xml中通過<Connector>元素設置線程池中線程的數目。
?
三、如何開發線程安全的Servlet ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ?1. 變量的線程安全:這里的變量指字段和共享數據(如表單參數值)。
? ? ?2. 將參數變量本地化,多線程并不共享局部變量,所以我們要盡可能的在servlet中使用局部變量,而不是實例變量。
? ? ? ? ?例如:String user = "";?user = request.getParameter("user");
? ? ?3. 使用同步塊Synchronized,防止可能異步調用的代碼塊,這意味著線程需要排隊處理。
? ? ? ? 在使用同板塊的時候要盡可能的縮小同步代碼的范圍,不要直接在sevice方法和響應方法上使用同步,這樣會嚴重影響性能。
? ? ?4.?javax.servlet.SingleThreadModel接口是一個標識接口,如果一個Servlet實現了這個接口,
? ? ? ? 那Servlet容器將保證在一個時刻僅有一個線程可以在給定的servlet實例的service方法中執行。將其他所有請求進行排隊。
? ? ? ? SingleThreadModel接口在servlet規范中已經被廢棄了。
import java.io.IOException; import java.io.PrintWriter;import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public class ThreadServlet extends HttpServlet {/*** servlet的線程安全 避免錯誤發生 * 1、實現 SingleThreadModel 接口 * 2、使用 Synchronized同步塊能保證一次只有一個線程可以訪問被保護的區段 * 3、使用局部變量*/private static final long serialVersionUID = -5054249585688590514L;private PrintWriter pw;public void doGet(HttpServletRequest req, HttpServletResponse res) {doPost(req, res);}public void doPost(HttpServletRequest req, HttpServletResponse res) {res.setCharacterEncoding("utf-8");String userName = req.getParameter("username");try {synchronized (this) {pw = res.getWriter();Thread.sleep(5000); //為了突出問題的明顯,設置線程休眠5秒pw.println("用戶名:" + userName);}} catch (IOException e1) {// TODO Auto-generated catch block e1.printStackTrace();} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}}} <form id="form1" method="post" action="<%=request.getContextPath()%>/ThreadServlet"><input type="text" name="username" /><input type="submit" /> </form>代碼解釋:
? ? ? 現在我們使用了局部變量PrintWriter pw;?
? ? ? 然后我們在service方法里使用了synchronized同步塊,這個servlet就是線程安全的,
? ? ? 如果我們不使用synchronized同步的話,如果我們在瀏覽器同時打開兩個url,例如:
? ? ? a線程?http://localhost:8080/MyJavaWeb/strutsdemo/threadservlet.jsp
? ? ? b線程?http://localhost:8080/MyJavaWeb/strutsdemo/threadservlet.jsp
? ? ? 然后輸入input后同時提交請求的話,問題就出來了:
| 調度時刻 | a線程 | b線程 |
| T1 | 訪問Servlet頁面 | ? |
| T2 | ? | 訪問Servlet頁面 |
| T3 | pw.println("用戶名:" + userName)輸出username=a休眠5000毫秒,讓出CPU | ? |
| T4 | ? | pw.println("用戶名:" + userName)輸出(寫回主存)username=b休眠5000毫秒,讓出CPU |
| T5 | 在用戶b的瀏覽器上輸出a線程的username的值,a線程終止。 | ? |
| T6 | ? | 在用戶b的瀏覽器上輸出b線程的username的值,b線程終止。 |
? ? ? 我們會發現兩個output都輸出在b線程的瀏覽器上了,這個在高并發的時候尤其要注意線程的安全。
?
四、屬性的線程安全
? ? ?1.?ServletContext是可以多線程同時讀/寫屬性的,線程是不安全的。要對屬性的讀寫進行同步處理或者進行深度Clone()。所以在Servlet上下文中盡可能少量保存會被修改(寫)的數據,可以采取其他方式在多個Servlet中共享,比方我們可以使用單例模式來處理共享數據。
? ? ?2. HttpSession對象在用戶會話期間存在,只能在處理屬于同一個Session的請求的線程中被訪問,因此Session對象的屬性訪問理論上是線程安全的。當用戶打開多個同屬于一個進程的瀏覽器窗口,在這些窗口的訪問屬于同一個Session,會出現多次請求,需要多個工作線程來處理請求,可能造成同時多線程讀寫屬性。
?? 這時我們需要對屬性的讀寫進行同步處理:使用同步塊Synchronized和使用讀/寫器來解決。
? ? 3. ServletRequest對于每一個請求,由一個工作線程來執行,都會創建有一個新的ServletRequest對象,所以ServletRequest對象只能在一個線程中被訪問,ServletRequest是線程安全的。
?? 注意:ServletRequest對象在service方法的范圍內是有效的,不要試圖在service方法結束后仍然保存請求對象的引用。
? ?Servlet本身就是多線程的,在Servlet中再創建線程,將導致執行情況復雜化,出現多線程安全問題。
轉載于:https://www.cnblogs.com/NicholasLee/archive/2012/07/08/2581606.html
總結
以上是生活随笔為你收集整理的servlet多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美媒:苹果强推混合现实头盔打破自家黄金规
- 下一篇: 阿里财报:国际零售增长41%,连续3个季