在JAVA平台,實現異步調用的角色有如下三個角色:
調用者 取貨憑證 真實數據
一個調用者在調用耗時操作,不能立即返回數據時,先返回一個取貨憑證.然后在過一斷時間后
憑取貨憑證來獲取真正的數據.
所以連結調用者和真實數據之間的橋梁是取貨憑證.我們先來看它的實現:
public class FutureTicket{
private Object data = null;
private boolean completed = false;
public synchronized void makeRealData(){
if(this.complited) return;
//獲取數據的耗時操作.這里用Sleep代替
try{
Thread.sleep(10000);
}catch(Throwable t){}
this.data = "返回的數據內容";
this.completed = true;
notifyAll();
}
public synchronized Object getData(){
while(!this.completed)){
try{
wait();
}catch(Throwable t){}
}
return this.data;
}
public boolean isCompleted(){
return this.completed;
}
}
為了簡單化說明(不把它們的關系開得復雜),這里用Objectb代替了真實數據.而真實的實現中
我們應該把makeData放在一個真實數據的類中,然后提供一個方法返回真實數據.這樣對於真實
數據的處理和取貨憑證解耦.
對於這個取貨憑證,調用者的如何調用是異步調用的關鍵:
publc class Requester{
public FutureTicket request(){
final FutureTicket ft = new FutureTicket();
//在新線程中調用耗時操作
new Thread(){
public void run(){
ft.makeRealData();
}
}.start();
return ft;
}
}
在新線程中啟動耗時操作后,不等待線程的完成立即返回提貨單.
然后調用者可以根據ft.isCompleted()來調用getData()獲取真實數據.
當然對ft.isCompleted()測試可以按規定時間間隔輪巡(極低級的方案),也可以
在條件不滿足時wait(),然后等待makeData的notifyAll();這樣你就完成了一個
用JAVA模擬的異步操作.
改進:
但這樣的調用對於調用者來說仍然要繼續控制線程操作.如果調用者是一個資深的
程序員,這當然沒有問題.但假如我們把對直接數據的處理委托給取貨憑證來做.調用
者直接規定對數據的操作,然后由取貨憑證來調用規定的操作,這對於調用者是一個很
好的解脫:
interface ProcessData{
public void process(Onject data);
}
public MyProcessData{
public void process(Object data){
//你不管什么時候起初數據data被獲取了.
//你只要規定如果獲取到數據了如何處理
System.out.println(data.toString() + "處理完成...........");
//insert into dataBase?
}
}
取貨憑證在接收調用者請求獲取數據時,要知道對獲取的數據如何處理的方法:
public class FutureTicket{
private Object data = null;
private boolean completed = false;
private ProcessData pd;
public FutureTicket(ProcessData pd){
this.pd = pd;
}
public synchronized void makeRealData(ProcessData pd){
if(this.complited) return;
//獲取數據的耗時操作.這里用Sleep代替
try{
Thread.sleep(10000);
}catch(Throwable t){}
this.data = "返回的數據內容";
this.completed = true;
notifyAll();
}
public synchronized void putData(){
while(!this.completed)){
try{
wait();
}catch(Throwable t){}
}
//return this.data;
//不用返回了,直接處理
this.pd.process(this.data);
// alert(?);
}
//這個方法也可以不要了.
public boolean isCompleted(){
return this.completed;
}
}
調用:
final FutureTicket ft = new FutureTicket(new ProcessData());
//在新線程中調用耗時操作
new Thread(){
public void run(){
ft.makeRealData();
}
}.start();
ft.putData();
}
在實際編程中,往往需要網絡的異步處理。比如客戶程序,如果客戶程序運行先於服務程序,則客戶程序則需要在服務程序啟動后再自動連接服務程序;在客戶程序運行中如果服務程序中途停止,則也需要在不停止的條件下,等待服務程序運行並重新連接。下面提供了一類異步編程的方法。
網絡異步應用涉及到如下幾個關鍵點:
客戶應用啟動后,檢測服務應用是否存在。如果不存在,則等待服務應用啟動,同時不堵塞客戶應用其他任務的執行。一旦服務應用啟動,客戶應用應該及時的與其建立連接。
客戶應用和服務應用在數據通信中,服務應用異常退出后,客戶應用應可以檢測到服務應用的退出。同時客戶應用自動清除該通信鏈路,回到初始狀態,等待服務應用重新啟動。
該網絡異步編程首先涉及到一個定時器和定時器事件。該定時器用於不斷的檢測網絡中客戶應用和服務應用是否連通,同時在服務應用出現異常時中止數據通信,返回到初始狀態。網絡的故障可以通過網絡方法的異常處理獲知。
定時器包含在網絡通信類中,使得使用該類的應用感知不到定時器的存在,而方便的處理網絡信息。
該客戶程序類如下結構:
public class NetComm implements ActionListener { javax.swing.Timer timer = new javax.swing.Timer(3000,this); Socket sock; private EventNotifier en; public static int net_state = 0; InetAddress ServerAddr; int ServerPort; public NetComm(InetAddress addr, int port){ ServerAddr = addr; ServerPort = port; } public void NetComm_Init() { net_state = 1; try { sock = new Socket(ServerAddr, ServerPort); } catch (IOException e) { net_state = 0; } timer.start(); } public void NetComm_Data() { try { OutputStream outputstream = sock.getOutputStream(); BufferedWriter out = new BufferedWriter (new OutputStreamWriter(outputstream)); out.write("java by piky2004@126.com"); out.flush(); BufferedReader in = new BufferedReader (new InputStreamReader(sock.getInputStream())); boolean more = true; while(more) { String str = in.readLine(); if(str == null) more = false; else // 處理數據 System.out.println(str); } in.close(); } catch (IOException e) { NetComm_Close(); net_state = 0; } timer.start(); } public void NetComm_Close() { if(sock != null) try{ sock.close(); } catch ( IOException e) { } } public void actionPerformed(ActionEvent e) { if(net_state == 0) NetComm_Init(); else NetComm_Data(); }
在上面的程序中,也可以為外部應用提供一個回調函數,以便在網絡異常或恢復正常時通知應用。服務應用的網絡通信類類似,可以放在同一類中。