2011-10-08 67 views
0

我創建一個的線程,每發送一個序列化對象來使用ObjectOutputStream一個服務器鏈接兩個線程在客戶端 - 服務器的Socket程序 - Java的

服務器每個套接字連接創建新線程(每當一個新的客戶端連接)

將調用一個同步方法上的共享資源互斥這導致它(B)到等待(),直到Mutex中的某些內部條件爲真。

在這種情況下如何A可以知道B目前正在等待?

希望這個描述很清楚。

上課安排:

A1--------->B1-------->|  | 
A2--------->B2-------->| Mutex | 
A3--------->B3-------->|  | 

編輯: 這是一個必須有等待(),通知()或notifyAll的(),因爲這是其中的併發測試學術項目。

+0

爲什麼A需要知道?B會阻止更長的時間嗎?如果是這樣,這種方法可能根本不會擴展...... –

+0

爲什麼需要A知道B在等待? – MasterCassim

+0

A需要知道B目前正在等待什麼?在發送序列化對象之後,A是否不等待B發送響應? –

回答

4

通常情況下,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代碼可能完全不知道您正在使用線程進行通信。

回覆:waitnotify等等。你不需要編寫自己的代碼來使用它們。使用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線程操縱。所有權明顯分離。數據作爲字節數組在線程之間傳遞。

如果您想支持多個請求同時發生,您可以啓動多個工作者。

+0

+1即時回覆。請看看我上面的評論。 – coder9

+0

我會給另一個+1。將需要一段時間來讀取這個,並告訴這是否解決了這個問題..我想要與等待的B鏈接的A,以防止用戶在該GUI上執行任何操作。我也嘗試過EDT的東西之前,你可以看到我以前的帖子都是關於這個:http://stackoverflow.com/questions/7696472/background-process-in-swingworker-causes-eventqueue-to-freeze-in-java- complete-c – coder9

+0

「handleReply」方法和「ServerReplyHandler」類的外觀如何? – coder9