許多Web應用、企業應用涉及到長時間的操作,例如復雜的數據庫查詢或繁重的XML處理等,雖然這些任務主要由數據庫系統或中間件完成,但任務執行的結果仍舊要借助JSP才能發送給用戶。本文介紹了一種通過改進前端表現層來改善用戶感覺、減輕服務器負載的辦法。
當JSP調用一個必須長時間運行的操作,且該操作的結果不能(在服務器端)緩沖,用戶每次請求該頁面時都必須長時間等待。很多時候,用戶會失去耐心,接著嘗試點擊浏覽器的刷新按鈕,最終失望地離開。
本文介紹的技術是把繁重的計算任務分離開來,由一個獨立的線程運行,從而解決上述問題。當用戶調用JSP頁面時,JSP頁面會立即返回,並提示用戶任務已經啟動且正在執行;JSP頁面自動刷新自己,報告在獨立線程中運行的繁重計算任務的當前進度,直至任務完成。
一、模擬任務
首先我們設計一個TaskBean類,它實現java.lang.Runnable接口,其run()方法在一個由JSP頁面(start.jsp)啟 動的獨立線程中運行。終止run()方法執行由另一個JSP頁面stop.jsp負責。TaskBean類還實現了 java.io.Serializable接口,這樣JSP頁面就可以將它作為JavaBean調用:
packagetest.barBean;
importjava.io.Serializable;
publicclassTaskBeanimplementsRunnable,Serializable{
privateintcounter;
privateintsum;
privatebooleanstarted;
privatebooleanrunning;
privateintsleep;
publicTaskBean(){
counter=0;
sum=0;
started=false;
running=false;
sleep=100;
}
}
TaskBean包含的"繁重任務"是計算 1+2+3…+100的值,不過它不通過100*(100+1)/2=5050公式計算,而是由run()方法調用work()方法100次完成計算。 work()方法的代碼如下所示,其中調用Thread.sleep()是為了確保任務總耗時約10秒。
protectedvoidwork(){
try{
Thread.sleep(sleep);
counter++;
sum+=counter;
}catch(InterruptedExceptione){
setRunning(false);
}
}
status.jsp頁面通過調用下面的getPercent()方法獲得任務的完成狀況:
publicsynchronizedintgetPercent(){
returncounter;
}
如果任務已經啟動,isStarted()方法將返回true:
publicsynchronizedbooleanisStarted(){
returnstarted;
}
如果任務已經完成,isCompleted()方法將返回true:
publicsynchronizedbooleanisCompleted(){
returncounter==100;
}
如果任務正在運行,isRunning()方法將返回true:
publicsynchronizedbooleanisRunning(){
returnrunning;
}
SetRunning()方法由start.jsp或stop.jsp調用,當running參數是true時。SetRunning()方法還要將任務標記為"已經啟動"。調用setRunning(false)表示要求run()方法停止執行。
publicsynchronizedvoidsetRunning(booleanrunning){
this.running=running;
if(running)
started=true;
}
任務執行完畢後,調用getResult()方法返回計算結果;如果任務尚未執行完畢,它返回null:
publicsynchronizedObjectgetResult(){
if(isCompleted())
returnnewInteger(sum);
else
returnnull;
}
當running標記為true、completed標記為false時,run()方法調用work()。在實際應用中,run()方法也許要執行復 雜的SQL查詢、解析大型XML文檔,或者調用消耗大量CPU時間的EJB方法。注意"繁重的任務"可能要在遠程服務器上執行。報告結果的JSP頁面有兩 種選擇:或者等待任務結束,或者使用一個進度條。
publicvoidrun(){
try{
setRunning(true);
while(isRunning()&&!isCompleted())
work();
}finally{
setRunning(false);
}
<