2013-02-18 54 views
2

我正在製作一個簡單的客戶端服務器程序,它允許用戶連接,更改其名稱並移入聊天室。如果服務器未處於活動狀態,服務器會週期性地向每個客戶端發送心跳信號,如果客戶端沒有響應,則會移除客戶端。從ConcurrentHashMap正確刪除值

爲了進一步加強我的服務器清理,我還定期檢查我的房間是否爲空,在這種情況下,我將服務器的空間從服務器上移除,以防止建立不必要的數據。但是,此刪除會產生問題。我使用ConcurrentHashMap將房間名稱映射到一個包含玩家名稱和套接字的ConcurrentHashMap。然後,我週期性地遍歷每個房間,檢查它是否包含任何玩家(大小> 0)。如果不是,我將房間移開。

但是,當服務器決定清理它時,當用戶選擇準確時刻加入空房間時,會出現非常有問題的情況。由於ConcurrentHashMap處理所有下面的同步,所以我無法同步這種特定情況,因此刪除是100%線程安全的。當房間被移走時,用戶可能會加入房間,這使得他陷入了僵局。

我該如何解決這個問題?

+0

如果你第一次從地圖中刪除房間,然後檢查是否有任何用戶?如果有,則重新將房間添加到地圖。如果只是在地圖中有一個新房間,並且名稱相同,則會合並這兩個房間。 – Luciano 2013-02-18 19:07:28

回答

3

我會保持外部映射爲ConcurrentHashMap,但用ChatRoom類替換內部映射。單個房間的預期活動率似乎並不能證明如此強大的併發地圖。

ChatRoom類應該是線程安全的,它應該有一個「關閉」標誌,指示房間是否已關閉。 close()方法應該使用房間的鎖來更改標誌,並使後續操作變得非法。實際上,close方法應該返回一個布爾值,指示房間是否已關閉;它應該是封閉的,當且僅當房間是空的。

你的空閒房間檢查線程應該調用room.close(),然後從外部地圖中刪除它。

+0

謝謝這是一個好主意。我不相信我沒有想到它。使用Apache或其他網絡API將使我不必完成這些工作。但我想自己做的是一個更好的學習過程。 – ImpGuard 2013-02-18 21:44:52

2

您可以向房間添加同步方法,如boolean closeIfEmpty()。如果成功,您可以安全地從地圖中刪除房間(使用2參數刪除方法)。如果添加代碼嘗試添加到封閉房間,則添加應該失敗,並且呼叫者創建新房間並替換封閉房間。

0

您可以在房間ConcurrentHashMap實例上同步刪除和「添加用戶到房間」操作,並在房間上保持「關閉」標誌。

喜歡的東西

void scanRoomAndRemove(Map room) { 
synchronized (room) { 
    // scan room, remove from parent Map if empty 
    room.put("closed",new Object()); 
} 
} 

void addPlayerToRoom(Player player,Map room) { 
synchronized(room) { 
    if (!room.containsKey("closed")) { 
    // add player to room 
    } else { 
    // whine here 
    } 
} 
}