簡體   English   中英

在DataOutputStream上同步

[英]Synchronize on DataOutputStream

我現在已經經歷了很多關於同步的教程,我的頭腦正在旋轉。 我從來沒有真正理解它:(。

我有一個Java服務器(MainServer),當客戶端連接創建一個新的線程(ServerThread)與DataOutputStream聯合。

客戶端與ServerThread通信ServerThread響應。 時不時的MainServer將分發消息利用每個ServerThread的 DataOutputStream類對象的所有客戶。

我敢肯定的是,每一個現在,然后我的問題是,因為無論是MainServerServerThread試圖在同一時間送東西給客戶端。 因此,我需要鎖定DataOutputStream對象。 對於我的生活,我無法進一步理解這個概念。 我讀到的每個例子都令人困惑。

處理這個問題的正確方法是什么?

ServerThread的發送到客戶端方法:

public void replyToOne(String reply){
    try {
        commandOut.writeUTF(reply);
        commandOut.flush();
    } catch (IOException e) {
        logger.fatal("replyToOne", e);
    }
    logger.info(reply);
}

MainServer分發給所有客戶端的方法:

public static void distribute(String broadcastMessage){
    for (Map.Entry<String, Object[]> entry : AccountInfoList.entrySet()) {
        Object[] tmpObjArray = entry.getValue();
        DataOutputStream temporaryCOut = (DataOutputStream) tmpObjArray[INT_COMMAND_OUT]; //can be grabbed while thread is using it
        try {
            temporaryCOut.writeUTF(broadcastMessage);
            temporaryCOut.flush();
        } catch (IOException e) {
            logger.error("distribute: writeUTF", e);
        }
        logger.info(broadcastMessage);  
    }
}

我想我的ServerThread類中應該有這樣的東西。

public synchronized DataOutputStream getCommandOut(){
    return commandOut;
}

它真的那么簡單嗎? 我知道這可能已經被問到並得到了回答,但如果沒有個人的幫助,我似乎沒有得到它。

如果這是我.....

我會在每個客戶端線程上有一個LinkedBlockingQueue 然后,每次客戶端線程在套接字上有空閑時,它都會檢查隊列。 如果有要從隊列發送的消息,它會發送它。

然后,服務器,如果需要,可以只添加項目到該隊列,並且,當連接有一些空間時,它將被發送。

添加隊列,在ServerThread上有一個方法:

addBroadcastMessage(MyData data) {
    broadcastQueue.add(data);
}

然后,在套接字方面,有一個在其上有超時塊的循環,以便它在空閑時突破套接字,然后只是:

while (!broadcastQueue.isEmpty()) {
    MyData data = broadcastQueue.poll();
    .... send the data....
}

你完成了

LinkedBlockingQueue將為您管理鎖定和同步。

你走在正確的軌道上。

修改DataOutputStream每個語句都應該在此DataOutputStreamsynchronized ,以便不會同時訪問它(因此沒有任何並發​​修改):

public void replyToOne(String reply){
    try {
        synchronized(commandOut) {    // writing block
            commandOut.writeUTF(reply);
            commandOut.flush();
        }
    } catch (IOException e) {
        logger.fatal("replyToOne", e);
    }
    logger.info(reply);
}

和:

public static void distribute(String broadcastMessage){
    for (Map.Entry<String, Object[]> entry : AccountInfoList.entrySet()) {
        Object[] tmpObjArray = entry.getValue();
        DataOutputStream temporaryCOut = (DataOutputStream) tmpObjArray[INT_COMMAND_OUT]; //can be grabbed while thread is using it
        try {
            synchronized(temporaryCOut) {  // writing block
                temporaryCOut.writeUTF(broadcastMessage);
                temporaryCOut.flush();
            }
        } catch (IOException e) {
            logger.error("distribute: writeUTF", e);
        }
    logger.info(broadcastMessage);  
    }
}

只需要我的2美分:

我實現服務器的方式是這樣的:

每個服務器都是一個只有一個任務的線程:偵聽連接。 一旦識別出連接,它就會生成一個新線程來處理連接的輸入/輸出(我稱之為子類ClientHandler)。

服務器還會保留所有已連接客戶端的列表。

ClientHandlers負責用戶 - 服務器交互。 從這里開始,事情非常簡單:

免責聲明:這里沒有嘗試捕獲塊! 自己添加它們。 當然,您可以使用線程執行程序來限制並發連接的數量。

服務器的run()方法:

@Override
public void run(){
 isRunning = true;
 while(isRunning){
  ClientHandler ch = new ClientHandler(serversocket.accept());
  clients.add(ch);
  ch.start();
 }
}

ClientHandler的ctor:

public ClientHandler(Socket client){
 out = new ObjectOutputStream(client.getOutputStream());
 in = new ObjectInputStream(client.getInputStream());
}

ClientHandler的run()方法:

@Override
public void run(){
 isConnected = true;
 while(isConnected){
  handle(in.readObject());
 }
}

handle()方法:

private void handle(Object o){
 //Your implementation
}

如果您想要一個統一的通道說輸出,那么您必須按照指示進行同步以避免意外結果。

有兩種簡單的方法可以做到這一點:

  1. 在synchronized(this)塊中包含每次調用輸出
  2. 使用getter輸出(就像你做的那樣)使用synchronized關鍵字。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM