January 21, 2021
Hot Topics:

Non-Blocking I/O Made Possible in Java

  • By Thribhuvan Thakur
  • Send Email »
  • More Articles »

Non-Blocking I/O Made Possible in Java

In this article we will review non-blocking IO, a feature of java.nio (New I/O) package that is a part of Java v1.4, v1.5 and v1.6 and introduce the java.nio.file (NIO.2) package. NIO.2 will be included in the upcoming Java SE7 ("Dolphin") release.

Prior to JDK1.4, threads engaged in network I/O exhibited the following characteristics:

  • " multiple threads for polling devices for readiness
  • required dedicating a thread to each socket connection
  • blocking until data is available

Classical Blocking Server

  class Server implements Runnable {
    public void run() {
    try {
       ServerSocket ss = new ServerSocket(PORT);
       while (!Thread.interrupted())
        new Thread(new Handler(ss.accept())).start();
        // one thread per socket connection
   // every thread created this way will essentially block for I/O
    } catch (IOException ex) { /* ... */ }

With functionality introduced in JSR-51, it is possible to set Channels in a non-blocking mode, under the watch of a Selector that has the ability to recognize/sense when one or more channels become available for data transfer. This frees the application from having to dedicate threads for blocking on devices awaiting data or implementing expensive polling processes. The following UML diagram shows the significant methods of Selector, SelectableChannel and SelectionKey.

Figure 3. Selector Topology

Some notes about non-blocking I/O:

  • In non-blocking mode, an I/O operation never blocks (methods return immediately),
  • In non -blocking mode, an I/O operation may transfer fewer bytes than were requested (partial read or write) or possibly no bytes at all.
  • FileChannel does not support non-blocking file operations.
  • Non-Blocking Polling API (JSR-51)is not the same as asynchronous API (JSR-203 - part of JDK 1.7).

The Selector for Single-Thread Multi-Socket I/O

A Selector allows processing of multiple sockets I/O read & write operations in a single thread, also known as multiplexing, solving the limitation of 'one dedicated thread per socket connection.' It manages SelectableChannels (gateway for registering/deregistering channels), SelectionKeys (glue that associates Selectors, Channels and their I/O events) and monitors concurrent thread executions involved in network operations.

Figure 5. Selector maintains SelectionKeys & Channel Bindings

A Selector is modeled after 'Reactor' role in the Reactor pattern--it responds to I/O events by dispatching the appropriate handler. The channels declare interest in I/O events with the Selector to be indicated when they are ready for I/O. Selector does not initiate the actual I/O operation, the channels do.

A Selector maintains three kinds of key-sets.

  1. key-set : keys representing the current channel registrations of this selector
  2. selected-key set: each key's channel was detected to be ready for at least one of the operations identified in the key's interest set during a prior selection operation.
  3. cancelled-key set: keys that have been cancelled but whose channels have not yet been deregistered

Using Selectors

When a thread is ready for I/O, use one of the selection methods--select(), select(timeout), or selectNow() to identify the channels that are ready to perform one or more of the operations previously declared in the interest set. The host operating system is queried for this status. Keys may be added to and removed from the key sets using interestOps(int). After a key change, a selection method should be invoked again to obtain status on the changed channels. While Selectors are safe for use by multiple concurrent threads, their key sets are not.

A more detailed discussion of keys and interest sets follows below.

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

open() creates a Selector object by calling SelectorProvider. openSelector()

select()/select(timeout) blocks until at least one channel is selected, or selector's wakeup method is invoked, or the current thread is interrupted or the given timeout period expires.

selectNow() is non-blocking but may return 0 if no channels are ready at the time of the call.

selectedKeys() fetches the Set of selected-keys. You could remove the keys from the Set by invoking the remove method of an iterator but should not add to the Set. Employ proper synchronization when using the selected-key set since it is not thread safe.

keys() fetches the keys containing the current channel registrations. Employ proper synchronization when using the key set since it is not thread safe.

Page 1 of 3

This article was originally published on September 16, 2009

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date