简体   繁体   English

正确地从ConcurrentHashMap中删除值

[英]Removing Values from ConcurrentHashMap Properly

I am making a simple client server program that allows users to connect, change their name, and move into rooms to chat. 我正在制作一个简单的客户端服务器程序,该程序允许用户连接,更改其名称以及进入聊天室。 The server periodically sends heartbeat signals to each client if they have not been active, and removing the client if they don't respond. 服务器会定期向每个客户端发送心跳信号(如果它们未处于活动状态),如果每个客户端均未响应,则将其删除。

To further enhance my server cleanup, I'm also periodically checking if my rooms are empty, in which case I remove the room from the server to prevent unnecessary data from building up. 为了进一步加强服务器清理,我还将定期检查我的房间是否空着,在这种情况下,我从服务器上删除了房间,以防止不必要的数据积累。 However, this removal creates a problem. 但是,此删除会带来问题。 I'm using ConcurrentHashMap that maps a room name to a ConcurrentHashMap that holds player names and their sockets. 我正在使用ConcurrentHashMap,它将房间名称映射到保存玩家名称及其插座的ConcurrentHashMap。 Then, periodically, I loop through every room, checking if it contains any players (has a size > 0) or not. 然后,定期地,我遍历每个房间,检查其中是否包含任何玩家(尺寸大于0)。 If not, I remove the room. 如果没有,我将房间移开。

However, this presents a very problematic situation when a user picks the exact moment to join the empty room when the server decides to clean it up. 但是,当服务器决定清理房间时,当用户选择加入空房间的确切时间时,这会带来非常麻烦的情况。 Since ConcurrentHashMap handles all the below-the-hood synchronization, I can't synchronize this specific situation such that removal is 100% thread safe. 由于ConcurrentHashMap处理所有幕后同步,因此我无法同步此特定情况,因此删除是100%线程安全的。 A user might join the room just as the room is being removed, which causes him to be stuck in a state of limbo. 用户可能正好在移开房间的同时加入房间,这导致他陷入了困境。

How can I fix this problem? 我该如何解决这个问题?

I would keep the outer map as ConcurrentHashMap, but replace the inner map with a ChatRoom class. 我将外部地图保留为ConcurrentHashMap,但将内部地图替换为ChatRoom类。 The expected activity rate in a single room does not seem to justify such a strong concurrent map. 单个房间中的预期活动率似乎无法证明如此强大的并发图是合理的。

The ChatRoom class should be thread safe, and it should have a "closed" flag that indicates whether the room has been closed. ChatRoom类应该是线程安全的,并且应具有“已关闭”标志,用于指示房间是否已关闭。 The close() method should use the room's lock to change the flag, and make any subsequent operations illegal. close()方法应使用房间的锁来更改标志,并使任何后续操作非法。 Actually the close method should return a boolean value indicating whether the room has been closed; 实际上,close方法应该返回一个布尔值,指示房间是否已经关闭; it should be closed if and only if the room was empty. 当且仅当房间为空时,才应将其关闭。

Your idle room checker thread should call room.close(), and then remove it from the outer map. 您的空闲房间检查器线程应调用room.close(),然后将其从外部映射中删除。

you could add a synchronized method to a Room, something like boolean closeIfEmpty() . 您可以将同步方法添加到Room中,例如boolean closeIfEmpty() if that succeeds, you can safely remove the room from the map (using the 2 argument remove method). 如果成功,则可以安全地从地图上移除房间(使用2参数remove方法)。 if the adding code attempts to add to a closed room, the add should fail, and the caller create a new room and replace the closed room. 如果添加代码尝试添加到封闭的房间,则添加操作将失败,并且调用方将创建一个新房间并替换该封闭的房间。

You can synchronize both the removal and 'add user to room' operations on the Room ConcurrentHashMap instance, and also keep 'closed' flag on the room. 您可以在Room ConcurrentHashMap实例上同步删除操作和“向房间添加用户”操作,还可以在房间上保持“ closed”标志。

Something like 就像是

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
  }
 }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM