I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In this article, we are going to explore the foundations of one of the key additional APIs of the new I/O (NIO2) in Java 7- asynchronous channel APIs.

This is first in a series of articles that will cover this particular topic.

The asynchronous channel APIs are an enhancement to the earlier new I/O (NIO) APIs that shipped with Java 1.4. To read about NIO Selectors, please follow this link.

Another enhancement to the NIO APIs is the new File System API. You can read more about its file operations and path operations on this site too.

To use the NIO2 asynchronous channels in our projects, we have to import the java.nio.channels package as the required classes are bundled in it:

import java.nio.channels.*;

2. How Asynchronous Channel APIs Work

The asynchronous channel APIs were introduced into the existing java.nio.channels package, simply put – by prefixing the class names with the word Asynchronous.

Some of the core classes include: AsynchronousSocketChannel, AsynchronousServerSocketChannel and AsynchronousFileChannel.

As you may have noticed, these classes are similar in style to the standard NIO channel APIs.

And, most API operations available to the NIO channel classes are also available in the new asynchronous versions. The main difference is that the new channels enable some operations to be executed asynchronously.

When an operation is initiated, the asynchronous channel APIs provide us with two alternatives for monitoring and controlling the pending operations. The operation can return java.util.concurrent.Future object or we can pass to it a java.nio.channels.CompletionHandler.

3. The Future Approach

A Future object represents a result of an asynchronous computation. Assuming we want to create a server to listen to client connections, we call the static open API on the AsynchronousServerSocketChannel and optionally bind the returned socket channel to an address:

AsynchronousServerSocketChannel server 
  = AsynchronousServerSocketChannel.open().bind(null);

We have passed in null so that the system can auto-assign an address. Then, we call the accept method on the returned server SocketChannel:

Future<AsynchronousSocketChannel> future = server.accept();

When we call the accept method of a ServerSocketChannel in the old IO, it blocks until an incoming connection is received from a client. But the accept method of an AsynchronousServerSocketChannel returns a Future object right away.

The generic type of the Future object is the return type of the operation. In our case above, it is AsynchronousSocketChannel but it could just as well have been Integer or String, depending on the ultimate return type of the operation.

We can use the Future object to query the state of the operation:

future.isDone();

This API returns true if the underlying operation already completed. Note that completion, in this case, may mean normal termination, an exception, or cancellation.

We can also explicitly check if the operation has been canceled:

future.isCancelled();

It only returns true if the operation was canceled before completing normally, otherwise, it returns false. Cancellation is performed by the cancel method:

future.cancel(true);

The call cancels the operation represented by the Future object. The parameter indicates that even if the operation has started, it can be interrupted. Once an operation has completed, it cannot be canceled

To retrieve the result of a computation, we use the get method:

AsynchronousSocketChannel client= future.get();

If we call this API before the operation completes, it will block until completion and then return the result of the operation.

4. The CompletionHandler Approach

The alternative to using Future to handle operations is a callback mechanism using the CompletionHandler class. The asynchronous channels allow a completion handler to be specified to consume the result of an operation:

AsynchronousServerSocketChannel listener
  = AsynchronousServerSocketChannel.open().bind(null);

listener.accept(
  attachment, new CompletionHandler<AsynchronousSocketChannel, Object>() {
    public void completed(
      AsynchronousSocketChannel client, Object attachment) {
          // do whatever with client
      }
    public void failed(Throwable exc, Object attachment) {
          // handle failure
      }
  });

The completed callback API is invoked when the I/O operation completes successfully. The failed callback is invoked if the operation has failed.

These callback methods accept other parameters – to allow us to pass in any data we think may be suitable to tag along with the operation. This first parameter is available as the second parameter of the callback method.

Finally, a clear scenario is – using the same CompletionHandler for different asynchronous operations. In this case, we’d benefit from tagging each operation to provide context when handling the results we will see this in action in the following section.

5. Conclusion

In this article, we have explored introductory aspects of the Asynchronous Channel APIs of Java NIO2.

To get all code snippets and the full source code for this article, you can visit the Github project.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS