我正在创建一个简单的客户端服务器程序,允许用户连接,更改其名称,并进入聊天室。如果服务器尚未处于活动状态,服务器会定期向每个客户端发送心跳信号,如果客户端没有响应,则会将其删除。
为了进一步增强我的服务器清理,我还会定期检查我的房间是否为空,在这种情况下,我会从服务器中删除房间以防止不必要的数据建立。但是,此删除会产生问题。我正在使用ConcurrentHashMap将房间名称映射到ConcurrentHashMap,后者保存玩家名称及其套接字。然后,我定期循环每个房间,检查它是否包含任何玩家(大小> 0)。如果没有,我会拆除房间。
然而,当用户选择在服务器决定清理它时加入空房间的确切时刻时,这会出现一个非常有问题的情况。由于ConcurrentHashMap处理所有引擎盖下的同步,我无法同步这种特定情况,因此删除是100%线程安全的。用户可能会在房间被移除时加入房间,这会导致他陷入困境。
如何解决此问题?
答案 0 :(得分:3)
我将外部地图保持为ConcurrentHashMap,但用ChatRoom类替换内部地图。单个房间的预期活动率似乎不足以证明如此强大的并发地图。
ChatRoom类应该是线程安全的,它应该有一个“关闭”标志,指示房间是否已关闭。 close()方法应该使用房间的锁来更改标志,并使任何后续操作非法。实际上close方法应该返回一个布尔值,指示房间是否已经关闭;当且仅当房间空了时才应关闭。
您的空闲房间检查程序线程应调用room.close(),然后将其从外部地图中删除。
答案 1 :(得分:2)
您可以向房间添加同步方法,例如boolean closeIfEmpty()
。如果成功,您可以安全地从地图中删除房间(使用2参数remove方法)。如果添加代码尝试添加到封闭的房间,则添加应该失败,并且调用者创建一个新房间并替换封闭的房间。
答案 2 :(得分: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
}
}
}