国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JSP > 正文

詳細解析JSP編程中進度條的設(shè)計實例

2024-09-05 00:20:23
字體:
供稿:網(wǎng)友
  許多web應(yīng)用、企業(yè)應(yīng)用涉及到長時間的操作,例如復雜的數(shù)據(jù)庫查詢或繁重的xml處理等,雖然這些任務(wù)主要由數(shù)據(jù)庫系統(tǒng)或中間件完成,但任務(wù)執(zhí)行的結(jié)果仍舊要借助jsp才能發(fā)送給用戶。本文介紹了一種通過改進前端表現(xiàn)層來改善用戶感覺、減輕服務(wù)器負載的辦法。

  當jsp調(diào)用一個必須長時間運行的操作,且該操作的結(jié)果不能(在服務(wù)器端)緩沖,用戶每次請求該頁面時都必須長時間等待。很多時候,用戶會失去耐心,接著嘗試點擊瀏覽器的刷新按鈕,最終失望地離開。

  本文介紹的技術(shù)是把繁重的計算任務(wù)分離開來,由一個獨立的線程運行,從而解決上述問題。當用戶調(diào)用jsp頁面時,jsp頁面會立即返回,并提示用戶任務(wù)已經(jīng)啟動且正在執(zhí)行;jsp頁面自動刷新自己,報告在獨立線程中運行的繁重計算任務(wù)的當前進度,直至任務(wù)完成。

  一、模擬任務(wù)

  首先我們設(shè)計一個taskbean類,它實現(xiàn)java.lang.runnable接口,其run()方法在一個由jsp頁面(start.jsp)啟動的獨立線程中運行。終止run()方法執(zhí)行由另一個jsp頁面stop.jsp負責。taskbean類還實現(xiàn)了java.io.serializable接口,這樣jsp頁面就可以將它作為javabean調(diào)用:

  
package test.barbean; import java.io.serializable; public class taskbean implements runnable, serializable{ private int counter; private int sum; private boolean started; private boolean running; private int sleep; public taskbean(){ counter = 0; sum = 0; started = false; running = false; sleep = 100; } }


  taskbean包含的“繁重任務(wù)”是計算1+2+3…+100的值,不過它不通過100*(100+1)/2=5050公式計算,而是由run()方法調(diào)用work()方法100次完成計算。work()方法的代碼如下所示,其中調(diào)用thread.sleep()是為了確保任務(wù)總耗時約10秒。

  
protected void work(){ try{ thread.sleep(sleep); counter++; sum += counter; } catch (interruptedexception e){ setrunning(false); } }


  status.jsp頁面通過調(diào)用下面的getpercent()方法獲得任務(wù)的完成狀況:

  
public synchronized int getpercent(){ return counter; }


  如果任務(wù)已經(jīng)啟動,isstarted()方法將返回true:

  
public synchronized boolean isstarted(){ return started; }


  如果任務(wù)已經(jīng)完成,iscompleted()方法將返回true:

  
public synchronized boolean iscompleted(){ return counter == 100; }


  如果任務(wù)正在運行,isrunning()方法將返回true:

  
public synchronized boolean isrunning(){ return running; }


  setrunning()方法由start.jsp或stop.jsp調(diào)用,當running參數(shù)是true時。setrunning()方法還要將任務(wù)標記為“已經(jīng)啟動”。調(diào)用setrunning(false)表示要求run()方法停止執(zhí)行。

  
public synchronized void setrunning(boolean running){ this.running = running; if (running) started = true; }


  任務(wù)執(zhí)行完畢后,調(diào)用getresult()方法返回計算結(jié)果;如果任務(wù)尚未執(zhí)行完畢,它返回null:

  
public synchronized object getresult(){ if (iscompleted()) return new integer(sum); else return null; }


  當running標記為true、completed標記為false時,run()方法調(diào)用work()。在實際應(yīng)用中,run()方法也許要執(zhí)行復雜的sql查詢、解析大型xml文檔,或者調(diào)用消耗大量cpu時間的ejb方法。注意“繁重的任務(wù)”可能要在遠程服務(wù)器上執(zhí)行。報告結(jié)果的jsp頁面有兩種選擇:或者等待任務(wù)結(jié)束,或者使用一個進度條。

  
public void run() { try { setrunning(true); while (isrunning() && !iscompleted()) work(); } finally { setrunning(false); } }


  二、啟動任務(wù)

  start.jsp是web.xml部署描述符中聲明的歡迎頁面,web.xml的內(nèi)容是:

  
<?xml version="1.0" encoding="gb2312"?> <!doctype web-app public "-//sun microsystems, inc.//dtd web application 2.3//en" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <welcome-file-list> <welcome-file>start.jsp</welcome-file> </welcome-file-list> </web-app>


  start.jsp啟動一個專用的線程來運行“繁重的任務(wù)”,然后把http請求傳遞給status.jsp。

  start.jsp頁面利用標記創(chuàng)建一個taskbean的實例,將scope屬性定義為session使得對于來自同一瀏覽器的http請求,其他頁面也能提取到同一個bean對象。start.jsp通過調(diào)用session.removeattribute("task")確保創(chuàng)建了一個新的bean對象,而不是提取一個舊對象(例如,同一個用戶會話中更早的jsp頁面所創(chuàng)建的bean對象)。

  下面是start.jsp頁面的代碼清單:

  
<% session.removeattribute("task"); %> <jsp:usebean id="task" scope="session" class="test.barbean.taskbean"/> <% task.setrunning(true); %> <% new thread(task).start(); %> <jsp:forward page="status.jsp"/>

  start.jsp創(chuàng)建并設(shè)置好taskbean對象之后,接著創(chuàng)建一個thread,并將bean對象作為一個runnable實例傳入。調(diào)用start()方法時新創(chuàng)建的線程將執(zhí)行taskbean對象的run()方法。

  現(xiàn)在有兩個線程在并發(fā)執(zhí)行:執(zhí)行jsp頁面的線程(稱之為“jsp線程”),由jsp頁面創(chuàng)建的線程(稱之為“任務(wù)線程”)。接下來,start.jsp利用調(diào)用status.jsp,status.jsp顯示出進度條以及任務(wù)的執(zhí)行情況。注意status.jsp和start.jsp在同一個jsp線程中運行。

  start.jsp在創(chuàng)建線程之前就把taskbean的running標記設(shè)置成了true,這樣,即使當jsp線程已開始執(zhí)行status.jsp而任務(wù)線程的run()方法尚未啟動,也能夠確保用戶會得到“任務(wù)已開始運行”的狀態(tài)報告。

  將running標記設(shè)置成true、啟動任務(wù)線程這兩行代碼可以移入taskbean構(gòu)成一個新的方法,然后由jsp頁面調(diào)用這個新方法。一般而言,jsp頁面應(yīng)當盡量少用java代碼,即我們應(yīng)當盡可能地把java代碼放入java類。不過本例中我們不遵從這一規(guī)則,把new thread(task).start()直接放入start.jsp突出表明jsp線程創(chuàng)建并啟動了任務(wù)線程。

  在jsp頁面中操作多線程必須謹慎,注意jsp線程和其它線程實際上是并發(fā)執(zhí)行的,就象在桌面應(yīng)用程序中,我們用一個線程來處理gui事件,另外再用一個或多個線程來處理后臺任務(wù)。

  不過在jsp環(huán)境中,考慮到多個用戶同時請求某一個頁面的情況,同一個jsp頁面可能會在多個線程中同時運行;另外,有時同一個用戶可能會向同一個頁面發(fā)出多個請求,雖然這些請求來自同一個用戶,它們也會導致服務(wù)器同時運行一個jsp頁面的多個線程。

  三、任務(wù)進度

  status.jsp頁面利用一個html進度條向用戶顯示任務(wù)的執(zhí)行情況。首先,status.jsp利用標記獲得start.jsp頁面創(chuàng)建的bean對象:

<jsp:usebean id="task" scope="session" class="test.barbean.taskbean"/>

  為了及時反映任務(wù)執(zhí)行進度,status.jsp會自動刷新。javascript代碼settimeout("location=′status.jsp′", 1000)將每隔1000毫秒刷新頁面,重新請求status.jsp,不需要用戶干預。   

<html> <head> <title>jsp進度條</title> <% if (task.isrunning()) { %> <script language="javascript"> settimeout("location=′status.jsp′", 1000); </script> <% } %> </head> <body>

  進度條實際上是一個html表格,包含10個單元,即每個單元代表任務(wù)總體的10%進度。   

<h1 align="center">jsp進度條</h1> <h2 align="center">

  結(jié)果:   

<%= task.getresult() %><br> <% int percent = task.getpercent();%> <%= percent %>% </h2> <table width="60%" align="center" border=1 cellpadding=0 cellspacing=2> <tr> <% for (int i = 10; i <= percent; i += 10){ %> <td width="10%" bgcolor="#000080"> </td> <% } %> <% for (int i = 100; i > percent; i -= 10){ %> <td width="10%"> </td> <%} %> </tr> </table>

  任務(wù)執(zhí)行情況分下面幾種狀態(tài):正在執(zhí)行,已完成,尚未開始,已停止:  

<table width="100%" border=0 cellpadding=0 cellspacing=0> <tr> <td align="center"> <% if (task.isrunning()) { %>

  正在執(zhí)行   

<% } else { %> <% if (task.iscompleted()) { %>

  完成   

<% } else if (!task.isstarted()){ %>

  尚未開始   

<% } else { %>

  已停止   

<% } %> <% } %> </td> </tr>

  頁面底部提供了一個按鈕,用戶可以用它來停止或重新啟動任務(wù):  

<tr> <td align="center"> <br> <% if (task.isrunning()) { %> <form method="get" action="stop.jsp"> <input type="submit" value="停止"> </form> <% } else { %> <form method="get" action="start.jsp"> <input type="submit" value="開始"> </form> <% } %> </td> </tr> </table> </body></html>

  只要不停止任務(wù),約10秒后瀏覽器將顯示出計算結(jié)果5050:

  四、停止任務(wù)

  stop.jsp頁面把running標記設(shè)置成false,從而停止當前的計算任務(wù):  

<jsp:usebean id="task" scope="session" class="test.barbean.taskbean"/> <% task.setrunning(false); %> <jsp:forward page="status.jsp"/>

  注意最早的java版本提供了thread.stop方法,但jdk從1.2版開始已經(jīng)不贊成使用thread.stop方法,所以我們不能直接調(diào)用thread.stop()。

  第一次運行本文程序的時候,你會看到任務(wù)的啟動有點延遲;同樣地,第一次點擊“停止”按鈕時也可以看到任務(wù)并沒有立即停止運行(特別是如果機器配置較低的話,延遲的感覺更加明顯),這些延遲都是由于編譯jsp頁面導致的。編譯好jsp頁面之后,應(yīng)答速度就要快多了。

  五、實際應(yīng)用

  進度條不僅使得用戶界面更加友好,而且對服務(wù)器的性能也有好處,因為進度條會不斷地告訴用戶當前的執(zhí)行進度,用戶不會再頻繁地停止并重新啟動(刷新)當前的任務(wù)。另一方面,創(chuàng)建單獨的線程來執(zhí)行后臺任務(wù)也會消耗不少資源,必要時可考慮通過一個線程池來實現(xiàn)thread對象的重用。另外,頻繁地刷新進度頁面也增加了網(wǎng)絡(luò)通信開銷,所以務(wù)必保持進度頁面簡潔短小。

  在實際應(yīng)用中,后臺執(zhí)行的繁重任務(wù)可能不允許停止,或者它不能提供詳細的執(zhí)行進度數(shù)據(jù)。例如,查找或更新關(guān)系數(shù)據(jù)庫時,sql命令執(zhí)行期間不允許中途停止??不過如果用戶表示他想要停止或中止任務(wù),程序可以在sql命令執(zhí)行完畢后回退事務(wù)。

  解析xml文檔的時候,我們沒有辦法獲知已解析內(nèi)容精確的百分比。如果用dom解析xml文檔,直到解析完成后才得到整個文檔樹;如果用sax,雖然可以知道當前解析的內(nèi)容,但通常不能確定還有多少內(nèi)容需要解析。在這些場合,任務(wù)的執(zhí)行進度只能靠估計得到。

  估計一個任務(wù)需要多少執(zhí)行時間通常是很困難的,因為它涉及到許多因素,即使用實際測試的辦法也無法得到可靠的結(jié)論,因為服務(wù)器的負載隨時都在變化之中。一種簡單的辦法是測量任務(wù)每次執(zhí)行所需時間,然后根據(jù)最后幾次執(zhí)行的平均時間估算。

  如果要提高估計時間的精確度,應(yīng)當考慮實現(xiàn)一種針對應(yīng)用特點的算法,綜合考慮多種因素,例如要執(zhí)行的sql語句類型、要解析的xml模式的復雜程度,等等。

  結(jié)束語:本文例子顯示出用jsp、java、html和javascript構(gòu)造進度條是相當容易的,真正困難的是如何將它用到實際應(yīng)用之中,特別是獲得后臺任務(wù)的進度信息,但這個問題沒有通用的答案,每一種后臺執(zhí)行的任務(wù)都有它自己的特點,必須按照具體情況具體分析。



發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 宁津县| 马关县| 平塘县| 安溪县| 青冈县| 余庆县| 鹤庆县| 南汇区| 南郑县| 宁蒗| 新丰县| 贺兰县| 黎城县| 大同县| 奉贤区| 铁力市| 凉城县| 霞浦县| 山西省| 平罗县| 宁化县| 双柏县| 长葛市| 昌吉市| 香河县| 茶陵县| 牙克石市| 阳朔县| 诸暨市| 崇左市| 枣阳市| 札达县| 罗定市| 云梦县| 柘城县| 视频| 辉县市| 方正县| 大渡口区| 台山市| 乌恰县|