posts - 188,comments - 176,trackbacks - 0

           

          JSP默認(rèn)是以多線程方式執(zhí)行的,這是JSP與ASP,PHP,PERL等腳本語言不一樣的地方,也是它的優(yōu)勢(shì)之一,但如果不注意多線程中的同步問題,會(huì)使所寫的JSP程序有難以發(fā)現(xiàn)的錯(cuò)誤。下面以一個(gè)例子說明JSP中的多線程問題及解決方法。

           

          一、JSP的中存在的多線程問題:

          當(dāng)客戶端第一次請(qǐng)求某一個(gè)JSP文件時(shí),服務(wù)端把該JSP編譯成一個(gè)CLASS文件,并創(chuàng)建一個(gè)該類的實(shí)例,然后創(chuàng)建一個(gè)線程處理CLIENT端的請(qǐng)求。如果有多個(gè)客戶端同時(shí)請(qǐng)求該JSP文件,則服務(wù)端會(huì)創(chuàng)建多個(gè)線程。每個(gè)客戶端請(qǐng)求對(duì)應(yīng)一個(gè)線程。以多線程方式執(zhí)行可大大降低對(duì)系統(tǒng)的資源需求,提高系統(tǒng)的并發(fā)量及響應(yīng)時(shí)間.對(duì)JSP中可能用的的變量說明如下:

          實(shí)例變量


          實(shí)例變量是在堆中分配的,并被屬于該實(shí)例的所有線程共享,所以不是線程安全的.


          JSP系統(tǒng)提供的8個(gè)類變量


          JSP中用到的OUT,Request,Response,Session,Config,Page,PageConxt是線程安全的,Application在整個(gè)系統(tǒng)內(nèi)被使用,所以不是線程安全的.


          局部變量


          局部變量在堆棧中分配,因?yàn)槊總€(gè)線程都有它自己的堆棧空間,所以是線程安全的.


          靜態(tài)類


          靜態(tài)類不用被實(shí)例化,就可直接使用,也不是線程安全的.


          外部資源:


          在程序中可能會(huì)有多個(gè)線程或進(jìn)程同時(shí)操作同一個(gè)資源(如:多個(gè)線程或進(jìn)程同時(shí)對(duì)一個(gè)文件進(jìn)行寫操作).此時(shí)也要注意同步問題.

          二、下面的例子存在的多線程問題:

          <%@ page import="
          javax.naming.*,
          java.util.*,
          java.sql.*,
          weblogic.common.*
          " %>

          <%@ page import=" 
          Javax.naming.*
          java.util.
          *
          java.sql.
          *
          Weblogic.common.
          * 
          " %> 



          <% 
          String name 
          String product; 
          long quantity; 


          name
          =request.getParameter("name"); 
          product
          =request.getParameter("product"); 
          quantity
          =request.getParameter("quantity"); /*(1)*/ 
          savebuy(); 
          %> 


          <%! 
          public void savebuy() 

          /*進(jìn)行數(shù)據(jù)庫操作,把數(shù)據(jù)保存到表中*/ 
          try 
          Properties props 
          = new Properties(); 
          props.put(
          "user","scott"); 
          props.put(
          "password","tiger"); 
          props.put(
          "server","DEMO"); 

          Driver myDriver 
          = (Driver) iver").newInstance(); 
          conn = myDriver.connect("JDBC:weblogic:Oracle", props); 
          stmt 
          = conn.createStatement(); 

          String inssql 
          = "insert into buy(empid, name, dept) values (?, ?, ?,?)"
          stmt 
          = conn.prepareStatement(inssql); 

          stmt.setString(
          1, name); 
          stmt.setString(
          2, procuct); 
          stmt.setInt(
          3, quantity); 
          stmt.execute(); 
          }
           
          catch (Exception e) 

          System.out.println(
          "SQLException was thrown: " + e.getMessage()); 
          }
           
          finally //close connections and { 
          try 
          if(stmt != null
          stmt.close(); 
          if(conn != null
          conn.close(); 
          }
           catch (SQLException sqle) 
          System.out.println(
          "SQLException was thrown: " + sqle.getMessage()); 
          }
           
          }
           

          %> 

          <%!
          public void  savebuy()
          {
              /*進(jìn)行數(shù)據(jù)庫操作,把數(shù)據(jù)保存到表中*/
              try {
                Properties props = new Properties();
                props.put("user","scott");
                props.put("password","tiger");
                props.put("server","DEMO");  

                Driver myDriver = (Driver) iver").newInstance();
                conn = myDriver.connect("jdbc:weblogic:oracle", props);
                stmt = conn.createStatement();
             
                String inssql = "insert into buy(empid, name, dept) values (?, ?, ?,?)";
                stmt = conn.prepareStatement(inssql);

                stmt.setString(1, name);
                stmt.setString(2, procuct);   
                stmt.setInt(3, quantity);
                stmt.execute();
              }
              catch (Exception e)
              {
                  System.out.println("SQLException was thrown: " + e.getMessage());
              }
              finally //close connections and     {
                  try {
                    if(stmt != null)
                      stmt.close();
                    if(conn != null)
                      conn.close();
                  } catch (SQLException sqle) {
                      System.out.println("SQLException was thrown: " + sqle.getMessage());
                  }
              }
          }
          %>

          上面的程序模擬網(wǎng)上購物中的一部分,把用戶在瀏覽器中輸入的用戶名,購買的物品名稱,數(shù)量保存到表BUY中。在savebuy()函數(shù)中用到了實(shí)例變量,所以它不是線程安全的.因?yàn)?程序中的每一條語句都不是原子操作,如name=request.getParameter("name");在執(zhí)行是會(huì)對(duì)應(yīng)多個(gè)機(jī)器指令,在任何時(shí)候都可能因系統(tǒng)調(diào)度而轉(zhuǎn)入睡眠狀態(tài),讓其他的線程繼續(xù)執(zhí)行.如果線程A在執(zhí)行到(1)的時(shí)候轉(zhuǎn)入睡眠狀態(tài),線程B開始執(zhí)行并改變QUANTITY的值,那么當(dāng)又到A執(zhí)行時(shí),它會(huì)從調(diào)用savebuy()函數(shù)開始執(zhí)行,這樣它保存到表中的QUANTITY是被線程B改過的值,那么線程A對(duì)應(yīng)的用戶所實(shí)際購買的數(shù)量與保持到表中的數(shù)據(jù)不一致.這是個(gè)很嚴(yán)重的問題.

          三、解決方法

           

          1.采用單線程方式

          在該JSP文件中加上: ,使它以單線程方式執(zhí)行,這時(shí),仍然只有一個(gè)實(shí)例,所有客戶端的請(qǐng)求以串行方 式執(zhí)行。這樣會(huì)降低系統(tǒng)的性能.

           2.對(duì)函數(shù)savebuy()加synchronized進(jìn)行線程同步,該JSP仍然以多線程方式執(zhí)行,但也會(huì)降低系統(tǒng)的性能

          public synchronized void savebuy() 

                  
          }
           

           3.采用局部變量代替實(shí)例變量

          函數(shù)savebuy()聲明如下:因?yàn)樵趕avebuy()中使用的是傳給他的形參,是在堆棧中分配的,所以是線程安全的

          public void savebuy(String name,String product, int quantity)
          {
                
          }

          調(diào)用方式改為:

          <% 
          String name 
          String product; 
          long quantity; 
          name
          =request.getParameter("name"); 
          product
          =request.getParameter("product"); 
          quantity
          =request.getParameter("quantity"); 
          savebuy(name,product,quantity) 
          %> 

          如果savebuy的參數(shù)很多,或這些數(shù)據(jù)要在很多地方用到,也可聲明一個(gè)類,并用他做參數(shù),如:

          public class buyinfo
          {
                String name;
                String product;
                
          long quantity;
          }
           
          public void savebuy(buyinfo info)
          {
                
          }

           調(diào)用方式改為:

          <% 
          buyinfo userbuy 
          = new buyinfo(); 

          userbuy.name
          =request.getParameter("name"); 
          userbuy.product
          =request.getParameter("product"); 
          userbuy.quantity
          =request.getParameter("quantity"); 
          savebuy(userbuy); 
          %>

           所以最好是用3,因?yàn)?,2會(huì)降低系統(tǒng)的性能. 多線程問題一般只有在在大并發(fā)量訪問時(shí),才有可能出現(xiàn),并且很難重復(fù)出現(xiàn),所以應(yīng)在編程時(shí)就時(shí)刻注意。


          轉(zhuǎn)CSDN

          posted on 2007-05-24 11:14 cheng 閱讀(278) 評(píng)論(0)  編輯  收藏 所屬分類: JSP/Servlet
          主站蜘蛛池模板: 嘉鱼县| 饶阳县| 虞城县| 哈密市| 凤山县| 阜城县| 杭锦后旗| 嘉黎县| 苏州市| 岳池县| 丰台区| 马公市| 唐海县| 漾濞| 乌拉特中旗| 深圳市| 新丰县| 富阳市| 鄂托克前旗| 容城县| 会东县| 柘荣县| 霍城县| 岳阳县| 故城县| 南江县| 威海市| 波密县| 曲阜市| 台南市| 日照市| 牟定县| 桦川县| 大英县| 凌源市| 济宁市| 赤壁市| 安乡县| 莒南县| 西平县| 陆丰市|