通常情況下,A會在套接字上讀取數據,這會「阻塞」(即不返回,掛斷),直到某些數據被B發送回來。它不需要被寫入來處理B的等待狀態它只是讀取而且固有地涉及等待某些東西讀取。
更新所以你希望A的用戶界面保持響應。到目前爲止,最好的方法是利用用戶界面庫的事件隊列系統。所有GUI框架都有一箇中心事件循環,用於將事件分派給處理程序(按鈕點擊,鼠標移動,計時器等)。後臺線程通常會將某些事情發佈到該事件隊列中,以便它將在主要UI線程。細節將取決於您使用的框架。
例如,在Swing中,後臺線程可以這樣做:
SwingUtilities.invokeAndWait(someRunnableObject);
因此,假設你定義了這個接口:
public interface ServerReplyHandler {
void handleReply(Object reply);
}
然後做一個很好的API爲您的GUI代碼時使用它想要向服務器提交請求:
public class Communications {
public static void callServer(Object inputs, ServerReplyHandler handler);
}
因此,您的客戶端代碼可以像這樣調用服務器:
showWaitMessage();
Communications.callServer(myInputs, new ServerReplyHandler() {
public void handleReply(Object myOutputs) {
hideWaitMessage();
// do something with myOutputs...
}
});
爲了實現上述API,你必須要求的對象,其存儲inputs
對象,併爲每個請求處理程序的線程安全的隊列。和一個後臺線程這只是什麼都不做,但是從隊列中提取請求,發送序列化的輸入到服務器,讀回的答覆,並deserialise,然後做到這一點:因此,一旦
final ServerReplyHandler currentHandler = ...
final Object currentReply = ...
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
currentHandler.handleReply(currentReply);
}
});
爲後臺線程已經回讀了回覆,它通過回調將它傳遞迴主UI線程。
這正是瀏覽器如何與JS代碼進行異步通信。如果你熟悉的jQuery,上述Communications.callServer
方法是相同的模式:
showWaitMessage();
$.get('http://...', function(reply) {
hideWaitMessage();
// do something with 'reply'
});
在這種情況下,唯一的區別是,你手寫整個通信棧。
更新2
你問:
你的意思是我可以通過如 在Communications.callServer 「myInputs」 「新的ObjectOutputStream()的writeObject(OBJ)。」?
如果所有信息都是作爲序列化對象傳遞的,則可以將序列化構建爲callServer
。調用代碼只是傳遞一些支持序列化的對象。 callServer
的執行會將該對象序列化爲byte[]
並將其發佈到工作隊列中。後臺線程會將其從隊列中彈出並將字節發送到服務器。
請注意,這樣可以避免序列化後臺線程上的對象。這樣做的好處是所有後臺線程活動都與UI代碼分開。 UI代碼可能完全不知道您正在使用線程進行通信。
回覆:wait
和notify
等等。你不需要編寫自己的代碼來使用它們。使用BlockingQueue接口的標準實現之一。在這種情況下,您可以使用LinkedBlockingQueue
與默認構造函數,以便它可以接受無限數量的項目。這意味着提交隊列總是會發生而不會阻塞。所以:
private static class Request {
public byte[] send;
public ServerReplyHandler handler;
};
private BlockingQueue<Request> requestQueue;
public static callServer(Object inputs, ServerReplyHandler handler) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
new ObjectOutputStream(byteStream).writeObject(inputs);
Request r = new Request();
r.send = byteStream.toByteArray();
r.handler = handler;
requestQueue.put(r);
}
同時在後臺輔助線程是這樣做的:
for (;;) {
Request r = requestQueue.take();
if (r == shutdown) {
break;
}
// connect to server, send r.send bytes to it
// read back the response as a byte array:
byte[] response = ...
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
currentHandler.handleReply(
new ObjectInputStream(
new ByteArrayInputStream(response)
).readObject()
);
}
});
}
的shutdown
變量只是:
private static Request shutdown = new Request();
即它是作爲一個特殊的信號假性請求。這允許您有另一個公共靜態方法來允許UI請求後臺線程退出(在將shutdown
放在它上面之前大概會清除隊列)。
請注意模式的要點:在後臺線程上永遠不會訪問UI對象。他們只能從UI線程操縱。所有權明顯分離。數據作爲字節數組在線程之間傳遞。
如果您想支持多個請求同時發生,您可以啓動多個工作者。
爲什麼A需要知道?B會阻止更長的時間嗎?如果是這樣,這種方法可能根本不會擴展...... –
爲什麼需要A知道B在等待? – MasterCassim
A需要知道B目前正在等待什麼?在發送序列化對象之後,A是否不等待B發送響應? –