Saturday, June 4, 2016

NIO.2 Asynchronous file I/O with Future and CompletionHandler

Recently I had a requirement to work with lot of I/O type of work and at the same time a lot of computation.So what Immediately a solution comes to mind that we will start a configurable number of threads.And the job of each thread will do the I/O independently and then after start the computation.But here one thing to observe that my computation has a very little to do with  the I/O.But in this design  my thread is not doing any  useful when it is doing the I/O and after the completion of the I/O the thread is going for computation.But it would be great if my thread can be free once it starts the I/O and without waiting to complete the I/O  jumps to the computation part.And some one  informs my thread once the I/O  completes.Till that my thread is busy with doing some useful calculation.

So here we get the two benefits.My thread is not waiting for I/O to complete and at the same time , it is doing some useful calculation.Here note that the job of my thread is both I/O bound and CPU bound.

Asynchronous  I/O :


NIO.2 provides support for  asynchronous  I/O(connecting, reading, and writing). In a synchronous  I/O, the thread that requests the I/O operation waits until the I/O operation  completes.In an asynchronous  I/O, the  application requests the system for an I/O operation and the operation is performed by the system asynchronously. When the system is performing the  I/O operation, the application continues doing some other useful computation  work. When the system finishes the  I/O, it notifies the application about the completion of I/O operation.


Four asynchronous channels are added in NIO.2 (java 7) to the java.nio.channels package:

  •     AsynchronousSocketChannel
  •     AsynchronousServerSocketChannel
  •     AsynchronousFileChannel
  •     AsynchronousDatagramChannel
       
Here  we take AsynchronousFileChannel  as our example and try to understand the asynchronous I/O.

The AsynchronousFileChannel provides us two different ways for monitoring and controlling the initiated asynchronous operations.

 The first one is by returning a java.util.concurrent.Future object, which poses a Future object and can be used to enquire its state and obtain the result.It follows a poll type approach.

The second is by passing to the  I/O operation an object of a new class, java.nio.channels.CompletionHandler, which defines handler methods that are executed after the operation is completed.It follows a push type approach.

Each method of the AsynchronousFileChannel class that supports asynchronous file I/O operation has two versions.One for Future object and another for CompletionHandler object.

Example of poll approach using Future object:



package com.brainatjava.test;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;
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 AshyncronousIOWithFuture {
static String str="write some meaning full text to file,which is desired for your applications.";
        public static void main(String[] args) {
        long startPosition=0;
        Path path = Paths.get("/home/brainatjava/mytest");
        try (AsynchronousFileChannel asyncFileChannel =
         AsynchronousFileChannel.open(path, WRITE, CREATE)) {
        ByteBuffer dataBuffer = ByteBuffer.wrap(str.getBytes());
        Future result = asyncFileChannel.write(dataBuffer, startPosition);
        while (!result.isDone()) {
        try {
        //remember in real life scenario the initiating thread will not sleep but it will  do some useful work.
        System.out.println("Sleeping for one seconds before the next pooling.We will continue to keep pooling in each one second.");
        Thread.sleep(1000);
        }
        catch (InterruptedException e) {
        e.printStackTrace();
        }
        }
       
        System.out.println("Now I/O operation is complete and we are going to get the result.");
        try {
        int resultbytewritten = result.get();
        System.out.format("%s bytes written to %s%n",
        resultbytewritten, path.toAbsolutePath());
        }
        catch (Exception e) {
        e.printStackTrace();
        }
        }
        catch (IOException e) {
        e.printStackTrace();
        }
        }
        }

In the example above first we create an AsynchronousFileChannel for writting. Then we use the write method to write some data,which return a Future object. Once we get a Future object, we  use a polling method method to handle the result of the asynchronous file I/O, where it keeps calling the isDone() method of the Future object to check if the I/O operation is finished or not.And rest of the code is self explanatory.But note that while checking the result of future object we are taking a 1 second sleep,    but in real life we  we will do some useful calculation there.

Example of push approach using CompletionHandler object:


 This version of the write method of the AsynchronousFileChannel class  allows us pass a CompletionHandler object whose methods are called when the requested asynchronous I/O operation completes or fails.

CompletionHandler interface is defined in the java.nio.channels package.

The type parameters:

    V – The result type of the I/O operation
    A – The type of the object attached to the I/O operation

The CompletionHandler interface has two methods: completed() and failed(). The completed() method is called when the requested I/O operation completes successfully. the failed() method is called ,when the requested I/O operation fails. The API allows  us to  pass an object of any type to the completed() and failed() methods. Such an object is called an attachment.We may want to pass an attachment such as the ByteBuffer or the reference to the channel or an reference to the I/O source etc. to these methods such that we can perform additional actions  inside these methods.For example we want to close the AsynchronousFileChannel once the async I/O operation completes successfully or fails due to any reason.We can also pass  null as an attachment , if we don't want to do anything usefull.
Lets create an Attachment object first

public class Attachment {

private Path filesource;
private AsynchronousFileChannel asyncChannel;


//getters and setters goes here.

}

Now let's define the CompletionHandler

private static class MyWriteCompletionHandler
implements CompletionHandler {
@Override
public void completed(Integer result, Attachment attachment) {
System.out.format("%s bytes written to %s%n",
result, attachment.path.toAbsolutePath());
try {
attachment.asyncChannel.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable e, Attachment attachment) {
System.out.format("I/O operation on %s file failed." +
"with  error is: %s", attachment.path, e.getMessage());
try {
attachment.asyncChannel.close();
}
catch (IOException e1) {
e1.printStackTrace();
}
}
}


public class ASyncIOWithCompletionHandler{

 public static void main(String[] args) {
static String str="write some meaning full text to file,which is desired for your applications.";
 Path path = Paths.get("/home/brainatjava/mytest");
 try {
AsynchronousFileChannel asyncfileChannel =
AsynchronousFileChannel.open(path, WRITE,CREATE);
MyWriteCompletionHandler handler = new MyWriteCompletionHandler();
ByteBuffer dataBuffer = ByteBuffer.wrap(str.getBytes());
Attachment attachment = new Attachment();
attachment.setAsyncChannel(asyncfileChannel);
attachment.setPath(path);
asyncfileChannel.write(dataBuffer, 0, attachment, handler);

try {
System.out.println("Sleeping for 10 seconds...");
Thread.sleep(10000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Completed");
}
catch (IOException e) {
e.printStackTrace();
}
 }
}

Here the main thread is  sleeping for 10 seconds ,but in real life scenario , the main thread will do some useful calculation rather than sleeping.

1 comment: