Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

concurrent handling of java.nio.channels.Selector

i'm working with java.nio.channels.Selector and i'd like to create a separate thread for each selectedKey that is ready for read/write/accept but i want to make sure that the same socket is never handled by two different threads simultaneously. what would be the best way to do it ? i was thinking to cancel each selectedKey before creating the thread that'll handle it's socket and re-registering the socket to the selector once the thread has finished it's life but i/m not sure how efficient this will be....

like image 231
yuri Avatar asked Dec 29 '22 06:12

yuri


2 Answers

There's a really good Doug Lea presentation about Scaleable I/O in Java, which I followed when building my server. I take the following approach:

I have a single I/O thread within my "reactor" that only performs I/O (and very basic decoding / encoding); It simply translates between bytes and message objects and then passes off incoming message objects to a thread pool for business logic processing. I would highly recommend this approach - Unless your I/O thread becomes saturated there is no need for more than one I/O thread, and I would imagine most I/O bottlenecks are because other processing it taking place on this thread.

If you can prove your I/O thread is saturated you could follow the "multiple reactor" pattern suggested in the presentation whereby a master reactor accepts incoming connections and then hands them off to child reactors that perform the procesing. Each child reactor multiplexes between a subset of the total connections, and hence there's no danger of more than one thread interacting with a given SelectionKey.

like image 98
Adamski Avatar answered Jan 12 '23 15:01

Adamski


I think creating a separate thread for each socket could end up being too many. Also, creating a new Thread is kind of expensive in execution time. You should cap the number of active threads and limit new thread creation by using a thread pool. java.util.concurrent.Executors offers the ability to create a fixed thread pool. Details in http://java.sun.com/docs/books/tutorial/essential/concurrency/pools.html .

If it's sockets you want to protect from being hit by multiple threads at one time, I'd consider the very simplest exclusion: Locking on the socket object. There could be more efficient strategies but probably none simpler or more foolproof.

Update

If another selection is done while some of the earlier returned sockets are still in processing, you could end up with threads interfering with each other. Shutting other threads out via locking is possible but not really an elegant solution (sorry).

Two alternatives I can think of:

  • deregister the channel before starting your processing thread on it, and re-register it at the end of the processing activity. Sounds klutzy but should get the job done.

  • maintain your own data structure of in-progress channels, e.g. a Set, and add a newly found-ready channel to that set before giving it to a thread, and remove it before returning from the thread. When processing channels from the selection set, ignore any that are already in the set. All use of this set will need to be synchronized.

like image 40
Carl Smotricz Avatar answered Jan 12 '23 14:01

Carl Smotricz