jsp默認是以多線程方式執行的,這是jsp與asp,php,perl等腳本語言不一樣的地方,也是它的優勢之一,但如果不注意多線程中的同步問題,會使所寫的jsp程序有難以發現的錯誤。下面以一個例子說明jsp中的多線程問題及解決方法。
一、jsp的中存在的多線程問題:
當客戶端第一次請求某一個jsp文件時,服務端把該jsp編譯成一個class文件,并創建一個該類的實例,然后創建一個線程處理client端的請求。如果有多個客戶端同時請求該jsp文件,則服務端會創建多個線程。每個客戶端請求對應一個線程。以多線程方式執行可大大降低對系統的資源需求,提高系統的并發量及響應時間.對jsp中可能用的的變量說明如下:
實例變量
實例變量是在堆中分配的,并被屬于該實例的所有線程共享,所以不是線程安全的.
jsp系統提供的8個類變量
jsp中用到的out,request,response,session,config,page,pageconxt是線程安全的,application在整個系統內被使用,所以不是線程安全的.
局部變量
局部變量在堆棧中分配,因為每個線程都有它自己的堆??臻g,所以是線程安全的.
靜態類
靜態類不用被實例化,就可直接使用,也不是線程安全的.
外部資源:
在程序中可能會有多個線程或進程同時操作同一個資源(如:多個線程或進程同時對一個文件進行寫操作).此時也要注意同步問題.
二、下面的例子存在的多線程問題:
<%@ 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() { /*進行數據庫操作,把數據保存到表中*/ 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()); } } } %>
上面的程序模擬網上購物中的一部分,把用戶在瀏覽器中輸入的用戶名,購買的物品名稱,數量保存到表buy中。在savebuy()函數中用到了實例變量,所以它不是線程安全的.因為:程序中的每一條語句都不是原子操作,如name=request.getparameter("name");在執行是會對應多個機器指令,在任何時候都可能因系統調度而轉入睡眠狀態,讓其他的線程繼續執行.如果線程a在執行到(1)的時候轉入睡眠狀態,線程b開始執行并改變quantity的值,那么當又到a執行時,它會從調用savebuy()函數開始執行,這樣它保存到表中的quantity是被線程b改過的值,那么線程a對應的用戶所實際購買的數量與保持到表中的數據不一致.這是個很嚴重的問題.
三、解決方法
采用單線程方式
在該jsp文件中加上: ,使它以單線程方式執行,這時,仍然只有一個實例,所有客戶端的請求以串行方 式執行。這樣會降低系統的性能.
對函數savebuy()加synchronized進行線程同步,該jsp仍然以多線程方式執行,但也會降低系統的性能
public synchronized void savebuy()
{
......
}
采用局部變量代替實例變量,函數savebuy()聲明如下:
因為在savebuy()中使用的是傳給他的形參,是在堆棧中分配的,所以是線程安全的.
public void savebuy(string name,string product, int quantity)
{
......
}
調用方式改為:
<%
string name
string product;
long quantity;
name=request.getparameter("name");
product=request.getparameter("product");
quantity=request.getparameter("quantity");
savebuy(name,product,quantity)
%>
如果savebuy的參數很多,或這些數據要在很多地方用到,也可聲明一個類,并用他做參數,如:
public class buyinfo
{
string name;
string product;
long quantity;
}
public void savebuy(buyinfo info)
{
......
}
調用方式改為:
<%
buyinfo userbuy = new buyinfo();
userbuy.name=request.getparameter("name");
userbuy.product=request.getparameter("product");
userbuy.quantity=request.getparameter("quantity");
savebuy(userbuy);
%>
所以最好是用3,因為1,2會降低系統的性能.
多線程問題一般只有在在大并發量訪問時,才有可能出現,并且很難重復出現,所以應在編程時就時刻注意。
新聞熱點
疑難解答