敬的世界

          常用鏈接

          統(tǒng)計(jì)

          最新評(píng)論

          在Servlet/JSP中的幾種變量類型

          源自 : http://www.javaresearch.org/article/8465.htm

          一、在Servlet/JSP中的幾種變量類型


          在編寫(xiě)Servlet/JSP程序時(shí),對(duì)實(shí)例變量一定要小心使用。因?yàn)閷?shí)例變量是非線程安全的。
          在Servlet/JSP中,變量可以歸為下面的幾類:

          1.?類變量
          request,response,session,config,application,以及JSP頁(yè)面內(nèi)置的page,?pageContext。
          其中除了application外,其它都是線程安全的。

          2.?實(shí)例變量
          實(shí)例變量是實(shí)例所有的,在堆中分配。在Servlet/JSP容器中,一般僅實(shí)例化一個(gè)Servlet/JSP實(shí)例,
          啟動(dòng)多個(gè)該實(shí)例的線程來(lái)處理請(qǐng)求。而實(shí)例變量是該實(shí)例所有的線程所共享,所以,實(shí)例變量不是線程安全的。

          3.?局部變量
          局部變量在堆棧中分配,因?yàn)槊恳粋€(gè)線程有自己的執(zhí)行堆棧,所以,局部變量是線程安全的。

          二、在Servlet/JSP中的多線程同步問(wèn)題


          在JSP中,使用實(shí)例變量要特別謹(jǐn)慎。首先請(qǐng)看下面的代碼:
          1. //?instanceconcurrenttest.jsp
          2. <%@?page?contentType="text/html;charset=GBK"?%>
          3. <%!
          4. ????//定義實(shí)例變量
          5. ????String?username;
          6. ????String?password;
          7. ????java.io.PrintWriter?output;
          8. %>
          9. <%
          10. ????//從request中獲取參數(shù)
          11. ????username?=?request.getParameter("username");
          12. ????password?=?request.getParameter("password");
          13. ????output?=?response.getWriter();
          14. ????showUserInfo();????
          15. %>
          16. <%!
          17. ????public?void?showUserInfo()?{
          18. ????????//為了突出并發(fā)問(wèn)題,在這兒首先執(zhí)行一個(gè)費(fèi)時(shí)操作
          19. ????????int?i?=0;
          20. ????????double?sum?=?0.0;
          21. ????????while?(i++?<?200000000)?{
          22. ????????????sum?+=?i;
          23. ????????}
          24. ????????
          25. ????????output.println(Thread.currentThread().getName()?+?"<br>");
          26. ????????output.println("username:"?+?username?+?"<br>");
          27. ????????output.println("password:"?+?password?+?"<br>");
          28. ????}
          29. %>
          在這個(gè)頁(yè)面中,首先定義了兩個(gè)實(shí)例變量,username和password。然后在從request中獲取這兩個(gè)參數(shù),并調(diào)用showUserInfo()方法將請(qǐng)求用戶的信息回顯在該客戶的瀏覽器上。在一個(gè)用戶訪問(wèn)是,不存在問(wèn)題。但在多個(gè)用戶并發(fā)訪問(wèn)時(shí),就會(huì)出現(xiàn)其它用戶的信息顯示在另外一些用戶的瀏覽器上的問(wèn)題。這是一個(gè)嚴(yán)重的問(wèn)題。為了突出并發(fā)問(wèn)題,便于測(cè)試、觀察,我們?cè)诨仫@用戶信息時(shí)執(zhí)行了一個(gè)模擬的費(fèi)時(shí)操作,比如,下面的兩個(gè)用戶同時(shí)訪問(wèn)(可以啟動(dòng)兩個(gè)IE瀏覽器,或者在兩臺(tái)機(jī)器上同時(shí)訪問(wèn)):
          a:????http://localhost:8080/instanceconcurrenttest.jsp?username=a&password=123
          b:????http://localhost:8080/instanceconcurrenttest.jsp?username=b&password=456

          如果a點(diǎn)擊鏈接后,b再點(diǎn)擊鏈接,那么,a將返回一個(gè)空白屏幕,b則得到a以及b兩個(gè)線程的輸出。請(qǐng)看下面的屏幕截圖:
          ?
          圖1:a的屏幕
          ?
          圖2:b的屏幕

          從運(yùn)行結(jié)果的截圖上可以看到,Web服務(wù)器啟動(dòng)了兩個(gè)線程分別來(lái)處理來(lái)自a和b的請(qǐng)求,但是在a卻得到一個(gè)空白的屏幕。這是因?yàn)樯厦娉绦蛑械膐utput,?username和password都是實(shí)例變量,是所有線程共享的。在a訪問(wèn)該頁(yè)面后,將output設(shè)置為a的輸出,username,password分別置為a的信息,而在a執(zhí)行printUserInfo()輸出username和password信息前,b又訪問(wèn)了該頁(yè)面,把username和password置為了b的信息,并把輸出output指向到了b。隨后a的線程打印時(shí),就打印到了b的屏幕了,并且,a的用戶名和密碼也被b的取代。請(qǐng)參加下圖所示:

          ?
          圖3:a、b兩個(gè)線程的時(shí)間線

          而實(shí)際程序中,由于設(shè)置實(shí)例變量,使用實(shí)例變量這兩個(gè)時(shí)間點(diǎn)非常接近,所以,像本例的同步問(wèn)題并沒(méi)有這么突出,可能會(huì)偶爾出現(xiàn),但這卻更加具有危險(xiǎn)性,也更加難于調(diào)試。

          同樣,對(duì)于Servlet也存在實(shí)例變量的多線程問(wèn)題,請(qǐng)看上面頁(yè)面的Servlet版:
          1. //?InstanceConcurrentTest.java
          2. import?javax.servlet.*;
          3. import?javax.servlet.http.*;
          4. import?java.io.PrintWriter;
          5. public?class?InstanceConcurrentTest?extends?HttpServlet?
          6. {
          7. ????String?username;
          8. ????String?password;
          9. ????PrintWriter?out;
          10. ????public?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)?
          11. ?????????throws?ServletException,java.io.IOException
          12. ????{
          13. ????????//從request中獲取參數(shù)
          14. ????????username?=?request.getParameter("username");
          15. ????????password?=?request.getParameter("password");
          16. ????????System.out.println(Thread.currentThread().getName()?+?
          17. ?????????????????????"?|?set?username:"?+?username);
          18. ????????out?=?response.getWriter();
          19. ????????showUserInfo();????
          20. ????}
          21. ????public?void?showUserInfo()?{
          22. ????????//為了突出并發(fā)問(wèn)題,在這兒首先執(zhí)行一個(gè)費(fèi)時(shí)操作
          23. ????????int?i?=0;
          24. ????????double?sum?=?0.0;
          25. ????????while?(i++?<?200000000)?{
          26. ????????????sum?+=?i;
          27. ????????}
          28. ????????out.println("thread:"?+?Thread.currentThread().getName());
          29. ????????out.println("username:"+?username);
          30. ????????out.println("password:"?+?password);
          31. ????}
          32. }

          三、解決方案


          1.?以單線程運(yùn)行Servlet/JSP


          在JSP中,通過(guò)設(shè)置:<%@?page?isThreadSafe="false"?%>,在Servlet中,通過(guò)實(shí)現(xiàn)javax.servlet.SingleThreadModel,此時(shí)Web容器將保證JSP或Servlet實(shí)例以單線程方式運(yùn)行。

          重要提示:在測(cè)試中發(fā)現(xiàn),Tomcat?4.1.17不能正確支持isThreadSafe屬性,所以,指定isTheadSafe為false后,在Tomcat?4.1.17中仍然出現(xiàn)多線程問(wèn)題,這是Tomcat?4.1.17的Bug。在Tomcat?3.3.1和Resin?2.1.5中測(cè)試通過(guò)。

          2.?去除實(shí)例變量,通過(guò)參數(shù)傳遞


          從上面的分析可見(jiàn),應(yīng)該在Servlet/JSP中盡量避免使用實(shí)例變量。比如,下面的修正代碼,去除了實(shí)例變量,通過(guò)定義局部變量,并參數(shù)進(jìn)行傳遞。這樣,由于局部變量是在線程的堆棧中進(jìn)行分配的,所以是線程安全的。不會(huì)出現(xiàn)多線程同步的問(wèn)題。代碼如下:
          1. <%@?page?contentType="text/html;charset=GBK"?%>
          2. <%
          3. ????//使用局部變量
          4. ????String?username;
          5. ????String?password;
          6. ????java.io.PrintWriter?output;
          7. ????//從request中獲取參數(shù)
          8. ????username?=?request.getParameter("username");
          9. ????password?=?request.getParameter("password");
          10. ????output?=?response.getWriter();
          11. ????showUserInfo(output,?username,?password);????
          12. %>
          13. <%!
          14. ????public?void?showUserInfo(java.io.PrintWriter?_output,?
          15. ?????????String?_username,?String?_password)?{
          16. ????????//為了突出并發(fā)問(wèn)題,在這兒首先執(zhí)行一個(gè)費(fèi)時(shí)操作
          17. ????????int?i?=0;
          18. ????????double?sum?=?0.0;
          19. ????????while?(i++?<?200000000)?{
          20. ????????????sum?+=?i;
          21. ????????}
          22. ????????
          23. ????????_output.println(Thread.currentThread().getName()?+?"<br>");
          24. ????????_output.println("username:"?+?_username?+?"<br>");
          25. ????????_output.println("password:"?+?_password?+?"<br>");
          26. ????}
          27. %>

          注:有的資料上指出在printUserInfo()方法或者實(shí)例變量的相關(guān)操作語(yǔ)句上使用synchronized關(guān)鍵字進(jìn)行同步,但這樣并不能解決多線程的問(wèn)題。因?yàn)椋@樣雖然可以使對(duì)實(shí)例變量的操作代碼進(jìn)行同步,但并不能阻止一個(gè)線程使用另外一個(gè)線程修改后的“臟的”實(shí)例變量。所以,除了降低運(yùn)行效率外,不會(huì)起到預(yù)期效果。

          posted on 2009-09-11 20:30 picture talk 閱讀(2010) 評(píng)論(1)  編輯  收藏 所屬分類: Servlet/Jsp

          評(píng)論

          # re: 在Servlet/JSP中的幾種變量類型 2011-09-02 10:19 GavinMiao

          路過(guò),剛好面試碰到,頂!
          轉(zhuǎn)載了。。。  回復(fù)  更多評(píng)論   


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 望城县| 罗江县| 兴宁市| 广西| 易门县| 清涧县| 阿巴嘎旗| 博湖县| 江源县| 集贤县| 广饶县| 平远县| 宜黄县| 鹰潭市| 石楼县| 喀喇沁旗| 林西县| 车致| 元阳县| 宜君县| 宾阳县| 梓潼县| 天长市| 永靖县| 华蓥市| 宜都市| 塘沽区| 娄底市| 讷河市| 宣恩县| 东光县| 武冈市| 城步| 江西省| 葵青区| 同江市| 永春县| 德庆县| 延津县| 翁牛特旗| 洛阳市|