简体   繁体   中英

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. Then, periodically, I loop through every room, checking if it contains any players (has a size > 0) or not. 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. 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. 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. The close() method should use the room's lock to change the flag, and make any subsequent operations illegal. Actually the close method should return a boolean value indicating whether the room has been closed; 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.

you could add a synchronized method to a Room, something like boolean closeIfEmpty() . if that succeeds, you can safely remove the room from the map (using the 2 argument remove method). 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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