傳遞回調(diào)函數(shù)
1、使用Javascript編寫
function complete(information){
console.log(information);
}
function servlet(command){
console.log("調(diào)用業(yè)務組件")
service(command,complete);
}
function service(command,callBack){
setTimeout(function(){
console.log(command);
callBack("業(yè)務組件完成調(diào)用");
},1000);
}
//用戶調(diào)用業(yè)務處理
servlet("select * from user_table where username = wangbinghua");
Servlet() 作為用戶調(diào)用的方法酬凳,其通知業(yè)務組件service(),并不知道業(yè)務組件何時調(diào)用完畢琐谤,因此將complete()回調(diào)函數(shù)作為參數(shù)檐蚜,調(diào)用業(yè)務組件魄懂。等待業(yè)務組件處理完畢業(yè)務之后,再次調(diào)用complete()函數(shù)闯第,表明已經(jīng)完成業(yè)務調(diào)用市栗。
2、使用Java編寫
在java中無法傳遞函數(shù)咳短,因此將接口作為參數(shù)進行傳遞填帽,從而達到傳遞函數(shù)的目的。
interface CallBack{
//回調(diào)函數(shù)
public void callBack(String result);
}
用戶主線程咙好,調(diào)用業(yè)務組件篡腌。而業(yè)務主線程驅(qū)動ServletProcess類的invokeService方法,在調(diào)用業(yè)務組件的同時勾效,開啟一條線程來處理業(yè)務邏輯嘹悼。因主線程驅(qū)動的ServletProcess類無法得知異步線程何時才能完成業(yè)務邏輯處理。所以层宫,將回調(diào)函數(shù)所在的接口作為參數(shù)傳遞給業(yè)務邏輯所處的異步線程杨伙。在異步線程完成之后,再次調(diào)用callBack方法卒密,表明業(yè)務邏輯完成處理缀台。
class ServletProcess implements CallBack{
private ServiceProcess serviceProcess;
public ServletProcess(ServiceProcess serviceProcess){
this.serviceProcess = serviceProcess;
}
public void dealOtherRequest(){
System.out.println("接受其他用戶的請求");
}
public void invokeService(final String information){
System.out.println("用戶線程開始:" + new Date());
//開啟異步線程調(diào)用業(yè)務處理組件(耗時)
new Thread(new Runnable() {
public void run() {
System.out.println("異步線程開始");
//調(diào)用業(yè)務組件
serviceProcess.dealService(information,ServletProcess.this);
}
}).start();
//接受其他用戶的請求
this.dealOtherRequest();
System.out.println("用戶線程結(jié)束:" + new Date());
}
//業(yè)務組件完成后,調(diào)用該方法
public void callBack(String result) {
System.out.println(result);
}
}
異步線程驅(qū)動的業(yè)務組件:
class ServiceProcess{
public void dealService(String information,ServletProcess servletProcess){
//處理業(yè)務
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("處理業(yè)務:" + information);
System.out.println("異步線程結(jié)束");
//處理完畢之后哮奇,通知ServletProcess組件
servletProcess.callBack("處理數(shù)據(jù)膛腐,渲染頁面");
}
}
3、類比Servlet異步處理
如果使用同步處理鼎俘,那么用戶每次請求一次哲身,就需要從線程池中獲取一個線程進行處理用戶的請求。那么在同步的條件下贸伐,都是由這一個線程同時進行請求處理和業(yè)務處理勘天。如果業(yè)務處理比較耗時,那么線程就會進行阻塞捉邢。此時脯丝,有更多的用戶進行請求,線程池中的線程在極端情況下全部阻塞伏伐,那么就無法處理用戶的請求宠进。用戶必須等待之前的業(yè)務處理的完成,很大程度上影響系統(tǒng)的吞吐量藐翎。因此提倡采用servlet的異步處理材蹬。
servlet通知完耗時業(yè)務組件處理業(yè)務之后实幕,馬上返回到線程池中,而不進行等待堤器。后續(xù)的操作由回調(diào)函數(shù)或者事件監(jiān)聽器完成昆庇。這樣,接下來更多的用戶請求闸溃,就會充分利用線程池中的線程整吆。
AsyncServlet異步調(diào)用業(yè)務組件處理業(yè)務邏輯,則其通知AsyncTask異步線程調(diào)用業(yè)務組件辉川,然后立即返回掂为。與此同時,Web容器線程將AsyncContext對象傳遞給AsyncTask異步線程员串。當異步線程處理業(yè)務完畢之后,將調(diào)用AsyncContext對象的complete方法或者dispach方法昼扛,表明業(yè)務處理完畢寸齐。
@WebServlet(name = "Servlet",urlPatterns = {"/us"},asyncSupported = true)
public class AsyncServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
asyncContext.start(new AsyncTask(asyncContext));
}
}
class AsyncTask implements Runnable{
private AsyncContext asyncContext;
public AsyncTask(AsyncContext asyncContext){
this.asyncContext = asyncContext;
}
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("deal some things!");
this.asyncContext.dispatch("/async.jsp");
// this.asyncContext.complete();
}
}
3、類比WebSocket異步處理
**
WebSocket的java服務器端要向客戶端發(fā)送消息抄谐,可能發(fā)送這個消息非常耗時渺鹦,那么此時會造成服務器端程序阻塞,使得服務器端的處理性能急劇下降蛹含。因此毅厚,可以對消息的發(fā)送進行異步處理。即WebSocket對應的Java API中的Async對象向服務器端發(fā)送消息時浦箱,調(diào)用send方法吸耿,其只是通知send方法,立即返回酷窥。異步線程(使用Future接口)來負責向客戶端發(fā)送消息咽安,此時容器主線程并不知道什么時候異步線程可以發(fā)送消息完畢。因此蓬推,在使用異步線程調(diào)用send方法的同時妆棒,將SendHandler接口傳遞給異步線程。當異步線程發(fā)送消息完畢時沸伏,則調(diào)用SendHandler接口的onResult方法糕珊,表明異步線程已經(jīng)發(fā)送消息完畢,從而讓容器主線程感知到毅糟。
**
@OnMessage
public void receiveMessage(Session session,String message,@PathParam("loginName") String loginName)
throws IOException {
System.out.println("服務器收到的信息為:" + message);
session.getAsyncRemote().sendText(SendInformationAsync.sendInfo(), new SendHandler() {
//服務器向客戶端發(fā)送數(shù)據(jù)完畢之后红选,則調(diào)用SendHandler接口的onResult方法
public void onResult(SendResult result) {
if(result.isOK()){
System.out.println("信息發(fā)送完畢");
}
}
});
}
使用監(jiān)聽器
1、使用Javascript編寫
用戶主線程調(diào)用servlet方法留特,而servlet方法調(diào)用業(yè)務組件service纠脾。此時用注冊一個事件的監(jiān)聽器得哆,即事件發(fā)生之后,調(diào)用callBack方法钧敞。用戶在servlet方法中調(diào)用service方法胳蛮,立即返回,并不知道service方法中的業(yè)務何時處理完成慧脱。利用事件監(jiān)聽器渺绒,在service方法中的業(yè)務處理完成之后,出發(fā)剛才注冊的事件菱鸥,即可調(diào)用callBack方法宗兼。
function servlet(command){
//調(diào)用業(yè)務組件
console.log("調(diào)用業(yè)務組件");
service(command);
}
function callBack(){
console.log("渲染頁面");
}
function service(command){
//業(yè)務組件
setTimeout(function(){
//處理業(yè)務
console.log("開始處理業(yè)務");
console.log(command);
console.log("處理業(yè)務完畢")
//觸發(fā)事件
$("#event").trigger("click");
},1000);
}
$("#event").on("click",callBack);
servlet("select * from user_table where username = wangbinghua");
2、類比servlet異步處理
在Web容器主線程中氮采,調(diào)用業(yè)務組件殷绍,注冊一個異步線程的監(jiān)聽器。該監(jiān)聽器主要監(jiān)聽四個事件鹊漠,success主到,timeout,error躯概,startAsync登钥。Web容器的主線程調(diào)用業(yè)務組件,則開啟一個異步線程娶靡,立即返回牧牢。主線程并不知道異步線程是否已經(jīng)完成了業(yè)務處理。因此姿锭,異步線程在完成了業(yè)務處理之后塔鳍,AsyncContext對象調(diào)用complete或者dispatch方法,即觸發(fā)了success事件呻此。觸發(fā)該事件之后献幔,立即調(diào)用監(jiān)聽器的onSuccess方法,表明異步線程已經(jīng)完成業(yè)務處理趾诗。
@WebServlet(name = "Servlet",urlPatterns = {"/us"},asyncSupported = true)
public class AsyncServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
final AsyncContext asyncContext = request.startAsync();
//注冊事件監(jiān)聽器
asyncContext.addListener(new AsyncListener() {
//異步線程業(yè)務處理完成之后蜡感,調(diào)用該方法
public void onComplete(AsyncEvent asyncEvent) throws IOException {
try {
asyncContext.getRequest().getRequestDispatcher("/async.jsp").
forward(asyncContext.getRequest(),asyncContext.getResponse());
} catch (ServletException e) {
e.printStackTrace();
}
System.out.println("異步線程完成");
}
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("onTimeout");
}
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("onError");
}
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("onStartAsync");
}
});
asyncContext.start(new AsyncTask(asyncContext));
}
}
class AsyncTask implements Runnable{
private AsyncContext asyncContext;
public AsyncTask(AsyncContext asyncContext){
this.asyncContext = asyncContext;
}
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("deal some things!");
// this.asyncContext.dispatch("/async.jsp");
this.asyncContext.complete();
}
}