October 30, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Non-Blocking I/O Made Possible in Java

  • September 16, 2009
  • By Thribhuvan Thakur
  • Send Email »
  • More Articles »

SelectionKey

When a Channel is registered with a Selector, a SelectionKey object is created to represent the registration, which includes the channel, the operational sets and the Selector that was provided during the registration process.

SelectionKey carries the event information necessary for event handler to process the event. As mentioned before, a Selector decouples event arrival from event handling, for example when a channel is ready to perform an I/O operation the Selector records the necessary information related to the event and it's bindings (channel, it's most recent interest set, ready-set etc.) in the SelectionKey and makes it available in the selection-set. The actual handling of the event starts when a SelectionKey is processed based on the caller's interest and ready sets.

Let's look at some important operations of a SelectionKey.

cancel() cancels the registration of this key's channel with its selector. It synchronizes on the selector's cancelled-key set, and therefore may block briefly if invoked concurrently with a cancellation or selection operation involving the same selector.

isValid() checks if the key is valid. A key is invalid if it is cancelled, its channel is closed, or its selector is closed

isReadable() / isWritable() / isConnectable() / isAcceptable() Each of the operations test the readiness of the channel. Call these methods (based on the type of operation you are interested in) soon after the selection process is completed. You could also bit-mask a specific type against the ready-set. For example, you could use the following expression similar to isConnectable().

key.readyOps() & SelectionKey.OP_CONNECT != 0

Operational Sets

A SelectionKey contains two operational sets (interest and ready), each represented as an integer bit-map. Each bit of an operation set denotes a category of selectable operations that are supported by the key's channel:

  • reading - OP_READ
  • writing - OP_WRITE
  • socket-accept - OP_ACCEPT
  • socket-connect - OP_CONNECT

The interest set identifies the operations for which the key's channel is monitored for by the Selector. This can be changed after the registration process using interestOps(int).

The ready-set identifies the operations the channel is ready to perform.

The current operational sets are available using interestOps() / readyOps().

Implementing Scalable Servers using Selectors

Here is a common way of implementing Servers using Selectors to manage I/O.

1. Create the ServerSocketChannel and the associated Selector

  public class Reactor implements Runnable{
   final ServerSocketChannel server; 
   final Selector selector; 
  
  Reactor(int port) throws IOException
  {
      server = ServerSocketChannel.open();
      selector = Selector.open(); 
  }

2. Enable non-blocking mode, bind the socket and register the ServerSocketChannel

  Reactor
  {
   …..        
   server.configureBlocking(false);
   server.socket().bind(new InetSocketAddress(remoteHost, port));
   SelectionKey sk = server.register(selector, SelectionKey.OP_ACCEPT);
   //attach any object if you would like with the key
  }

3. Initiate the selection process by calling one of select operations on the selector.
This one thread blocks for all configured I/O!

  public void run() { 
  try {
  while (!Thread.interrupted()) {
     selector.select(); //only this one thread blocks 
            //freeing other threads from blocking
   ……..
  }
  }catch( IOException ex)

4. When Key sets are updated by selector, it is time to grab the keys
Selector detects/discovers channels that are ready to initiate specific I/O operations.

  while(..)
  {
   ………..
   Set selected = selector.selectedKeys();
   Iterator itr = selected.iterator();
   while (itr.hasNext())
        dispatch((SelectionKey)(itr.next()); //starts separate threads 
   selected.clear(); //clear the keys from the set since they are already processed
  }

5. Process the key based on the operation the channel is ready to perform A) Key.isAcceptable()

  • Accept the incoming connection
  • Turn on the non-blocking mode of the channels
  • Register the client channels with the Selector based on ability of the server to read and write

//dispatch method spawns new threads based on the type of the key.
// none of the threads inside this method should block for I/O. 
void dispatch(SelectionKey key) 
{
  ………………
 if(key.isAcceptable()) // server is ready to accept a client channel
  { 
     //start a new thread that run the following code snippet
     SocketChannel clientChannel = server.accept(); 
     //key.channel() gives ServerSocketChannel
     clientChannel.configureBlocking(false);
     clientChannel.register(selector, SelectionKey.OP_READ | OP_WRITE);
     selector.wakeUp(); 

B) Key.isReadable()

   void dispatch(SelectionKey key) 
    {
     ………………
     if(key.isReadable())
    {
       //start reading from the channel in a separate thread
      performRead( (SocketChannel)key.channel() );
    }
 

C) Key.isWritable()

  void dispatch(SelectionKey key) 
   {
      ………………
     //start writing to the channel in a separate thread
     peformWrite ( SocketChannel)key.channel() );
  }





Page 2 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel