Home > Articles > Programming > Java

  • Print
  • + Share This
Like this article? We recommend

Like this article? We recommend

Asynchronous I/O

Asynchronous I/O

JSR 51 introduced multiplexed I/O (a combination of non-blocking I/O and readiness selection) to facilitate the creation of highly scalable servers. Basically, client code registers a socket channel with a selector in order to be notified when the channel is ready to begin I/O.

JSR 203 introduces asynchronous I/O, which is also used to facilitate the creation of highly scalable servers. Unlike multiplexed I/O, asynchronous I/O lets client code initiate an I/O operation, and subsequently notifies the client when the operation is complete.

Asynchronous I/O is implemented by the following interfaces and classes, which are located in the java.nio.channels package—their names are prefixed with the word Asynchronous:

  • AsynchronousChannel identifies a channel that supports asynchronous I/O.
  • AsynchronousByteChannel identifies an asynchronous channel that can read and write bytes. This interface extends the AsynchronousChannel interface.
  • AsynchronousDatagramChannel identifies an asynchronous channel for datagram-oriented sockets. This class implements AsynchronousByteChannel.
  • AsynchronousFileChannel identifies an asynchronous channel for reading, writing, and manipulating a file. This class implements AsynchronousChannel.
  • AsynchronousServerSocketChannel identifies an asynchronous channel for stream-oriented listening sockets. This class implements AsynchronousChannel.
  • AsynchronousSocketChannel identifies an asynchronous channel for stream-oriented connecting sockets. This class implements AsynchronousByteChannel.
  • AsynchronousChannelGroup identifies a grouping of asynchronous channels for the purpose of resource sharing.

The AsynchronousChannel documentation identifies two forms of asynchronous I/O operations:

  • Future<V> operation(...)
  • void operation(... A attachment, CompletionHandler<V, ? super A> handler)

operation names the I/O operation (such as read), V is the operation's result type, and A is the type of object attached to the operation to provide context when consuming the result—context is important where stateless completion handlers consume the results of many operations.

The first form requires you to call java.util.concurrent.Future methods to check for operation completion, to wait for completion, and to retrieve the result. Listing 2's application source code provides a demonstration.

Listing 2—AFCDemo1.java

// AFCDemo1.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Future;
public class AFCDemo1
{
   public static void main (String [] args) throws Exception
   {
      if (args.length != 1)
      {
          System.err.println ("usage: java AFCDemo1 path");
          return;
      }
      Path path = Paths.get (args [0]);
      AsynchronousFileChannel ch = AsynchronousFileChannel.open (path);
      ByteBuffer buf = ByteBuffer.allocate (1024);
      Future<Integer> result = ch.read (buf, 0);
      while (!result.isDone ())
      {
         System.out.println ("Sleeping...");
         Thread.sleep (500);
      }
      System.out.println ("Finished = "+result.isDone ());
      System.out.println ("Bytes read = "+result.get ());
      ch.close ();
   }
}

After invoking AsynchronousFileChannel's public static AsynchronousFileChannel open(Path file, OpenOption... options) method to open the file argument for reading, Listing 2 creates a byte buffer to store the results of the read operation.

Listing 2 next invokes the public abstract Future<Integer> read(ByteBuffer dst, long position) method to asynchronously read up to the first 1024 bytes from the file. This method returns a Future instance representing the pending result of this operation.

After invoking read(), Listing 2 enters a polling loop: It repeatedly invokes the Future's isDone() method to check for the operation's completion, and sleeps until the read finishes. Eventually, Future's get() method is invoked to return a count of bytes read.

The second form requires you to specify a java.nio.channels.CompletionHandler and implement the following methods to consume the result of an operation when it completes, or to learn why the operation failed and take appropriate action:

  • void completed(V result, A attachment) is invoked when the operation completes. The operation's result is identified by result, and the object attached to the operation when it was initiated is identified by attachment.
  • void failed(Throwable exc, A attachment) is invoked when the operation fails. The reason why the operation failed is identified by exc, and the object attached to the operation when it was initiated is identified by attachment.

I've created an application that demonstrates creating and receiving notification of a read operation's status via a completion handler. This application's source code appears in Listing 3.

Listing 3—AFCDemo2.java

// AFCDemo2.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
public class AFCDemo2
{
   static Thread current;
   public static void main (String [] args) throws Exception
   {
      if (args.length != 1)
      {
          System.err.println ("usage: java AFCDemo1 path");
          return;
      }
      Path path = Paths.get (args [0]);
      AsynchronousFileChannel ch = AsynchronousFileChannel.open (path);
      ByteBuffer buf = ByteBuffer.allocate (1024);
      current = Thread.currentThread ();
      ch.read (buf, 0, null,
               new CompletionHandler<Integer, Void> ()
               {
                   public void completed (Integer result, Void v)
                   {
                      System.out.println ("Bytes read = "+result);
                      current.interrupt ();
                   }
                   public void failed (Throwable exc, Void v)
                   {
                      System.out.println ("Failure: "+exc.toString ());
                      current.interrupt ();
                   }
               });
      System.out.println ("Waiting for completion");
      try
      {
          current.join ();
      }
      catch (InterruptedException e)
      {
      }
      System.out.println ("Terminating");
      ch.close ();
   }
}

Listing 3 invokes AsynchronousFileChannel's public abstract <A> void read(ByteBuffer dst, long position, A attachment, CompletionHandler<Integer,? super A> handler) method to asynchronously read up to the first 1024 bytes.

Because we're focusing on only a single read operation, the attachment portion of the completion handler isn't important. Therefore, Listing 3 passes null as the attachment to the read() method, and specifies Void as the attachment type.

To learn more about asynchronous I/O (along with other NIO.2 features), I recommend that you check out Ron Hitchens’ What's New in NIO.2 PDF document.

  • + Share This
  • 🔖 Save To Your Account