Jawad
Jawad

Reputation: 408

Iterate map keys while insert/remove is going on the map by other threads

I have a multi client server application in java. The server keeps on receiving connections and each client is handled by a separate thread. The client/server communication goes on until the socket is closed. So the request received from clients is put in a LinkedBlockingQueue and then other thread process each request from that queue. Since the client request is added to the queue I am using a ConcurrentHashMap to get the clientsocket later on when the request is processed and response is ready so that i can send the response to client later.

Now I need to implement a timeout functionality so if the request is not process and response is not ready within a time period then some sort of message is sent to the client that your request cannot be processed now. Can anybody tell me the best idea to do it in a multithreaded environment. Remember that I have a client map in which client connection is put against each request id.

I am thinking to have a separate thread that will keep on iterating the map keys and check the time. But since request keep on adding in the map I want some best way to do it.

Thanks

Upvotes: 3

Views: 155

Answers (2)

allprog
allprog

Reputation: 16790

Guava's loading cache can solve the timeout and concurrent modifications for you: https://code.google.com/p/guava-libraries/wiki/CachesExplained Exchange your request map to a LoadingCache by setting it up like this:

LoadingCache<Request, Connection> requests = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterAccess(1, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build(
           new CacheLoader<Request, Connection>() {
             public Connection load(Request request) throws AnyException {
               return clientConnectionForRequest(request);
             }
           });

When a request comes in, you load it in the cache:

requests.get(request);

After this, the request will sit there waiting to be processed. If processing is started, then get the connection and invalidate the request, so it is removed from the cache. ①

Connection c = requests.getIfPresent(request);
if (c != null) {
  requests.invalidate(request); // remove from the waiting area
  // proceeed with processing the request
} else {
  // the request was evicted from the cache as it expired
}

In the removal listener you need to implement some simple logic that listens for evictions. (If you invalidate explicitly, then wasEvicted() will return false.)

MY_LISTENER = new RemovalListener<Request, Connection>() {
    @Override
    public void onRemovaRequest RemovalNotification<Request, Connection> notification) {
        if (notification.wasEvicted()) {
            Connection c = notification.getValue();
            // send timeout response to client 
        }
    }
};

You can order the requests by placing them in a queue and executing the method described at ① That method will also take care of executing only those requests that did not time out yet, you need no additional house keeping.

Upvotes: 2

Vineet Kasat
Vineet Kasat

Reputation: 1014

Use Concurrent Hash Map. It allows full concurrency for reads and adjustable concurrency for writes. It uses volatile variables to put the data. Even if any modification is being made by any thread to a bucket, it will be visible to any other thread trying to read the datafrom the same bucket.

Upvotes: 1

Related Questions