[英]Synchronize on DataOutputStream
我現在已經經歷了很多關於同步的教程,我的頭腦正在旋轉。 我從來沒有真正理解它:(。
我有一個Java服務器(MainServer),當客戶端連接創建一個新的線程(ServerThread)與DataOutputStream聯合。
客戶端與ServerThread通信 , ServerThread響應。 時不時的MainServer將分發消息利用每個ServerThread的 DataOutputStream類對象的所有客戶。
我敢肯定的是,每一個現在,然后我的問題是,因為無論是MainServer和ServerThread試圖在同一時間送東西給客戶端。 因此,我需要鎖定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
每個語句都應該在此DataOutputStream
上synchronized
,以便不會同時訪問它(因此沒有任何並發修改):
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
}
如果您想要一個統一的通道說輸出,那么您必須按照指示進行同步以避免意外結果。
有兩種簡單的方法可以做到這一點:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.