傳遞回調(diào)函數(shù)
1、使用Javascript編寫
function complete(information){
console.log(information);
}
function servlet(command){
console.log("調(diào)用業(yè)務(wù)組件")
service(command,complete);
}
function service(command,callBack){
setTimeout(function(){
console.log(command);
callBack("業(yè)務(wù)組件完成調(diào)用");
},1000);
}
//用戶調(diào)用業(yè)務(wù)處理
servlet("select * from user_table where username = wangbinghua");

Servlet() 作為用戶調(diào)用的方法,其通知業(yè)務(wù)組件service(),并不知道業(yè)務(wù)組件何時調(diào)用完畢,因此將complete()回調(diào)函數(shù)作為參數(shù),調(diào)用業(yè)務(wù)組件。等待業(yè)務(wù)組件處理完畢業(yè)務(wù)之后,再次調(diào)用complete()函數(shù),表明已經(jīng)完成業(yè)務(wù)調(diào)用。
2、使用Java編寫

在java中無法傳遞函數(shù),因此將接口作為參數(shù)進行傳遞,從而達到傳遞函數(shù)的目的。
interface CallBack{
//回調(diào)函數(shù)
public void callBack(String result);
}
用戶主線程,調(diào)用業(yè)務(wù)組件。而業(yè)務(wù)主線程驅(qū)動ServletProcess類的invokeService方法,在調(diào)用業(yè)務(wù)組件的同時,開啟一條線程來處理業(yè)務(wù)邏輯。因主線程驅(qū)動的ServletProcess類無法得知異步線程何時才能完成業(yè)務(wù)邏輯處理。所以,將回調(diào)函數(shù)所在的接口作為參數(shù)傳遞給業(yè)務(wù)邏輯所處的異步線程。在異步線程完成之后,再次調(diào)用callBack方法,表明業(yè)務(wù)邏輯完成處理。
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è)務(wù)處理組件(耗時)
new Thread(new Runnable() {
public void run() {
System.out.println("異步線程開始");
//調(diào)用業(yè)務(wù)組件
serviceProcess.dealService(information,ServletProcess.this);
}
}).start();
//接受其他用戶的請求
this.dealOtherRequest();
System.out.println("用戶線程結(jié)束:" + new Date());
}
//業(yè)務(wù)組件完成后,調(diào)用該方法
public void callBack(String result) {
System.out.println(result);
}
}
異步線程驅(qū)動的業(yè)務(wù)組件:
class ServiceProcess{
public void dealService(String information,ServletProcess servletProcess){
//處理業(yè)務(wù)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("處理業(yè)務(wù):" + information);
System.out.println("異步線程結(jié)束");
//處理完畢之后,通知ServletProcess組件
servletProcess.callBack("處理數(shù)據(jù),渲染頁面");
}
}
3、類比Servlet異步處理

如果使用同步處理,那么用戶每次請求一次,就需要從線程池中獲取一個線程進行處理用戶的請求。那么在同步的條件下,都是由這一個線程同時進行請求處理和業(yè)務(wù)處理。如果業(yè)務(wù)處理比較耗時,那么線程就會進行阻塞。此時,有更多的用戶進行請求,線程池中的線程在極端情況下全部阻塞,那么就無法處理用戶的請求。用戶必須等待之前的業(yè)務(wù)處理的完成,很大程度上影響系統(tǒng)的吞吐量。因此提倡采用servlet的異步處理。
servlet通知完耗時業(yè)務(wù)組件處理業(yè)務(wù)之后,馬上返回到線程池中,而不進行等待。后續(xù)的操作由回調(diào)函數(shù)或者事件監(jiān)聽器完成。這樣,接下來更多的用戶請求,就會充分利用線程池中的線程。

AsyncServlet異步調(diào)用業(yè)務(wù)組件處理業(yè)務(wù)邏輯,則其通知AsyncTask異步線程調(diào)用業(yè)務(wù)組件,然后立即返回。與此同時,Web容器線程將AsyncContext對象傳遞給AsyncTask異步線程。當異步線程處理業(yè)務(wù)完畢之后,將調(diào)用AsyncContext對象的complete方法或者dispach方法,表明業(yè)務(wù)處理完畢。
@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服務(wù)器端要向客戶端發(fā)送消息,可能發(fā)送這個消息非常耗時,那么此時會造成服務(wù)器端程序阻塞,使得服務(wù)器端的處理性能急劇下降。因此,可以對消息的發(fā)送進行異步處理。即WebSocket對應(yīng)的Java API中的Async對象向服務(wù)器端發(fā)送消息時,調(diào)用send方法,其只是通知send方法,立即返回。異步線程(使用Future接口)來負責(zé)向客戶端發(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("服務(wù)器收到的信息為:" + message);
session.getAsyncRemote().sendText(SendInformationAsync.sendInfo(), new SendHandler() {
//服務(wù)器向客戶端發(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è)務(wù)組件service。此時用注冊一個事件的監(jiān)聽器,即事件發(fā)生之后,調(diào)用callBack方法。用戶在servlet方法中調(diào)用service方法,立即返回,并不知道service方法中的業(yè)務(wù)何時處理完成。利用事件監(jiān)聽器,在service方法中的業(yè)務(wù)處理完成之后,出發(fā)剛才注冊的事件,即可調(diào)用callBack方法。
function servlet(command){
//調(diào)用業(yè)務(wù)組件
console.log("調(diào)用業(yè)務(wù)組件");
service(command);
}
function callBack(){
console.log("渲染頁面");
}
function service(command){
//業(yè)務(wù)組件
setTimeout(function(){
//處理業(yè)務(wù)
console.log("開始處理業(yè)務(wù)");
console.log(command);
console.log("處理業(yè)務(wù)完畢")
//觸發(fā)事件
$("#event").trigger("click");
},1000);
}
$("#event").on("click",callBack);
servlet("select * from user_table where username = wangbinghua");
2、類比servlet異步處理

在Web容器主線程中,調(diào)用業(yè)務(wù)組件,注冊一個異步線程的監(jiān)聽器。該監(jiān)聽器主要監(jiān)聽四個事件,success,timeout,error,startAsync。Web容器的主線程調(diào)用業(yè)務(wù)組件,則開啟一個異步線程,立即返回。主線程并不知道異步線程是否已經(jīng)完成了業(yè)務(wù)處理。因此,異步線程在完成了業(yè)務(wù)處理之后,AsyncContext對象調(diào)用complete或者dispatch方法,即觸發(fā)了success事件。觸發(fā)該事件之后,立即調(diào)用監(jiān)聽器的onSuccess方法,表明異步線程已經(jīng)完成業(yè)務(wù)處理。
@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è)務(wù)處理完成之后,調(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();
}
}