2

我正在制作一个简单的客户端服务器程序,它允许用户连接、更改他们的名字并进入房间聊天。如果客户端没有活动,服务器会定期向每个客户端发送心跳信号,如果客户端没有响应,则删除客户端。

为了进一步加强我的服务器清理,我还会定期检查我的房间是否是空的,在这种情况下,我会从服务器上删除房间以防止不必要的数据堆积。然而,这种移除会产生一个问题。我正在使用 ConcurrentHashMap 将房间名称映射到包含玩家名称及其套接字的 ConcurrentHashMap。然后,我周期性地遍历每个房间,检查它是否包含任何玩家(大小> 0)。如果没有,我删除房间。

但是,当服务器决定清理空房间时,当用户选择加入空房间的确切时间时,这会出现一个非常有问题的情况。由于 ConcurrentHashMap 处理所有底层同步,因此我无法同步这种特定情况,以便删除是 100% 线程安全的。用户可能会在房间被移除时加入房间,这会导致他陷入困境。

我该如何解决这个问题?

4

3 回答 3

3

我会将外部地图保留为 ConcurrentHashMap,但将内部地图替换为 ChatRoom 类。单个房间的预期活动率似乎无法证明如此强大的并发地图是合理的。

ChatRoom 类应该是线程安全的,并且应该有一个“已关闭”标志来指示房间是否已关闭。close() 方法应该使用房间的锁来更改标志,并使任何后续操作非法。实际上 close 方法应该返回一个布尔值,指示房间是否已关闭;当且仅当房间是空的时才应该关闭它。

您的空闲房间检查器线程应调用 room.close(),然后将其从外部地图中删除。

于 2013-02-18T19:14:52.503 回答
2

您可以向 Room 添加同步方法,例如boolean closeIfEmpty(). 如果成功,您可以安全地从地图中移除房间(使用 2 参数移除方法)。如果添加代码试图添加到一个封闭的房间,添加应该失败,调用者创建一个新的房间并替换封闭的房间。

于 2013-02-18T19:12:58.083 回答
0

您可以在 Room 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
  }
 }
}
于 2013-02-18T19:18:20.577 回答