nio

Java Nio Tutorial for Beginners

Photo of JJJJMay 5th, 2017Last Updated: March 16th, 2021
0 343 8 minutes read

This article is a beginner’s tutorial on Java NIO (New IO). We will take a high-level look at this API which provides an alternative to Java IO. The Java NIO API can be viewed here. The example code demonstrates the use of the core abstractions in this topic.

Java nio tutorials examples in this article was built and run using:

  • Java 1.8.101 (1.8.x will do fine)
  • Maven 3.3.9 (3.3.x will do fine)
  • Spring source tool suite 4.6.3 (Any Java IDE would work)
  • Ubuntu 16.04 (Windows, Mac or linux)

 

1. Introduction

Since Java 1.4 the Java NIO API has provided an alternate method of dealing with IO operations. Why did we need an alternate method for doing IO? As time progresses new problem sets arrive and new approaches to solving these problems are thought of. To understand the need for an alternate means of IO handling one should probably understand the core differences between the two approaches.

IO

NIO

Core differences:Core differences:
Stream oriented processingUses buffers
Blocking in processingNon blocking in processing
Good for:Good for:
High data volume with low simultaneous open file descriptor counts

 

(eg: less client connections with more data chunks per connection)

Less data volume with high simultaneous open file descriptor counts

 

(eg: More connections with smaller / infrequent “chunks” of data)

NIO puts us in a position to make more judicious use of server / machine resources. By bringing us closer to the metal with an intelligent selection of abstractions we are able to better apply finite server resources to meet the increasing demands of modern day scale.

2. Java NIO

A quick glance at the summary of the Java NIO API reveals to us the core abstractions one should be familiar with when working with Java NIO. These are:

  • Buffers       : A container to hold data for the purposes of reading and or writing.
  • Channels   : An abstraction for dealing with an open connection to some component that is performing some kind of IO operation at a hardware level.
  • Charsets    : Contains charsets, decoders and encoders for translating between bytes and unicode.
  • Selectors   : A means to work with multiple channels via one abstraction.

2.1 Buffers

Java Nio - Buffer class hierarchy
Shows the Buffer class hierarchy

Buffer is a container for a fixed size of data of a specific primitive type (char, byte, int, long, float etc). ABuffer has content, a position, a limit and capacity. It can flip, rewind, mark and reset its position reinforcing the core differences between NIO and IO (buffer vs stream).

  • Capacity = number of elements it contains.
  • Limit = index of element that must not be read or written.
  • Position = index of next element to read or write.
  • Flip = invert position and limit when toggling the IO operation on aBuffer. (eg: write out to console what we just read from aChannel into the Buffer).
  • Rewind = sets position to 0 and leaves limit unchanged in order to re-read theBuffer.
  • Mark = bookmarks a position in theBuffer.
  • Reset = resets the position to the previous mark.

What does all that mean? Well basically we put content into aBuffer (either read it from aChannel or put it directly into the Buffer with the intent to write it to aChannel).

We then advance the cursor through the content of theBuffer  as we read or write. We flip aBuffer to change our IO operation on theBuffer (ie: go from reading to writing).

The capacity represents the total capacity theBuffer can hold with regard to content. The actual metric used for measurement depends on the type of theBuffer. (eg:CharBuffer capacity measured in characters andByteBuffer capacity measured in Bytes).

2.1.1 Java nio tutorial Example usage of a ByteBuffer

Reading from Channel into ByteBuffer

01
02
03
04
05
06
07
08
09
10
11
...
finalByteBuffer buffer = createBuffer();
while(fileChannel.read(buffer) != -1) {
    contents.append(newString(buffer.array()));
    buffer.clear();
}
...
privateByteBuffer createBuffer() {
    returnByteBuffer.allocate(BYTE_BUFFER_LENGTH);
}
...
  • line 2: AByteBuffer is created with a defined capacity. (BYTE_BUFFER_LENGTH)
  • line 3: Data is read from the specifiedFileChannel into theByteBuffer.
  • line 4: TheByteBuffer’s current contents are added to theStringBuilder. This is done via convenience methodarray() as a result of the way theByteBuffer was created in the example (viaallocate()).
  • line 5: TheByteBuffer is cleared to prepare for reading more data from the channel, this will set the position cursor back to 0 and allow contents to be read from theFileChannel back into theByteBuffer repeating the process until no more data is available.

Alternate method for reading from Channel into ByteBuffer 

1
2
3
4
5
6
7
8
...
buffer.flip();
if(buffer.hasRemaining()) {
    byte[] src =newbyte[buffer.limit()];
    buffer.get(src);
    contents.append(newString(src));
}
....
  • line 2: Invert the position and limit of theBuffer to retrieve what has been read from theChannel.
  • line 3: Ensure there is something to read, ie: The difference between limit and position is > 0.
  • line 4: Create a byte array to be the size of the data in theBuffer.
  • line 5: Retrieve the contents of theBuffer into the byte array.
  • line 6: Create aString array from the contents of the byte array.

It is important to also note that the instantiation of a newString to hold the bytes implicitly uses the defaultCharset to decode the bytes from their byte values to their corresponding unicode characters. If the defaultCharset was not what we were looking for, then instantiating a newString with the appropriateCharset would be required.

2.2 Channels

Java Nio - Interface hierarchy
Interface hierarchy of Channel

AChannel is a proxy (open connection proxy) to a component that is responsible for native IO (file or network socket). By acting as a proxy to some native IO component we are able to write and / or read from aChannel. SomeChannel implementations allow us to put them into non-blocking mode allowing read and write operations to be non-blocking. The sameChannel can be used for both reading and writing.

AChannel is open upon creation and remains that way until it is closed.

2.2.1 Example usage of a FileChannel

Creating a FileChannel

1
2
3
4
5
6
...
 
finalFile file =newFile(FileChannelReadExample.class.getClassLoader().getResource(path).getFile());
returnfileOperation == FileOperation.READ ?newFileInputStream(file).getChannel() :
                        newFileOutputStream(file).getChannel();
...
  • line 3: Create aFile Object
  • line 4: Depending on the type ofFile operation (read or write) we create the necessary Stream and get theChannel from the Stream.

2.3 Charsets

ACharset is a mapping between 16 bit unicode characters and bytes. Charsets work with decoders and encoders which facilitate the adaption from bytes to characters and vice versa.

  • Encoding: The process of transforming a sequence of characters into bytes
  • Decoding: The process of transforming bytes into character buffers.

Charset provides other utility methods for looking up aCharset by name, creating coders (encoder or decoders) and getting the default Charset. Typically when one works withByteBuffer andString as is the case in the example, the defaultCharset is what we would normally use if we do not explicitly specify one. This would suffice most of the time.

Charset usage

01
02
03
04
05
06
07
08
09
10
11
12
13
...
finalCharset defaultCharset = Charset.defaultCharset();
finalString text ="Lorem ipsum";
  
finalByteBuffer bufferA = ByteBuffer.wrap(text.getBytes());
finalByteBuffer bufferB = defaultCharset.encode(text);
  
finalString a =newString(bufferA.array());
finalCharBuffer charBufferB = defaultCharset.decode(bufferB);
  
System.out.println(a);
System.out.println(newString(charBufferB.array()));
...
  • line 2: The defaultCharset is retrieved.
  • line 5: The sample text is wrapped in aByteBuffer. The defaultCharset is used implicitly when encoding the characters into bytes.
  • line 6: The sample text is encoded explicitly using the defaultCharset encoder.
  • line 8: AString is created using the defaultCharset decoder implicitly .
  • line 9: A Character Buffer (ultimately a String) is created using the defaultCharset decoder explicitly.

2.4 Selectors

Selectors as the name implies, select from multipleSelectableChannel types and notify our program when IO has happened on one of those channels. It is important to note that during the registration process (registering aSelectableChannel with aSelector) we declare the IO events we are interested in, termed the “interest set” This can be:

Want to be a Java NIO Master ?
Subscribe to our newsletter and download the JDBCUltimateGuideright now!
In order to help you master Java NIO Library, we have compiled a kick-ass guide with all the major Java NIO features and use cases! Besides studying them online you may download the eBook in PDF format!

Thank you!

We will contact you soon.

  1. Connect
  2. Accept
  3. Read
  4. Write

With this proxy in place and the added benefit of setting thoseSelectableChannel types into non-blocking mode we are able tomultiplex over said channels in a very efficient way, typically with very few threads, even as little as one.

Selector usage with SelectableChannel

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
try(finalSelector selector = Selector.open();
     finalServerSocketChannel serverSocket = ServerSocketChannel.open();) {
    finalInetSocketAddress hostAddress =
          newInetSocketAddress(Constants.HOST, Constants.PORT);
    serverSocket.bind(hostAddress);
    serverSocket.configureBlocking(false);
    serverSocket.register(selector, serverSocket.validOps(),null);
 
    while(true) {
       finalintnumSelectedKeys = selector.select();
       if(numSelectedKeys >0) {
           handleSelectionKeys(selector.selectedKeys(), serverSocket);
       }
    }
}

The following code snippet demonstrates iterating through all theSelectionKey instances that indicate IO “ready” events fromChannel instances managed by the singleSelector. We are only interested in “Accept” and Readable” events. For every new connection accepted an “Accept” event is signaled and we can act on it. Likewise with a “read” ready event we can read incoming data. It is important to remove the  SelectionKey from the set after handling it, as theSelector does not do this and you will continue to process that stale event.

Working with SelectionKeys

01
02
03
04
05
06
07
08
09
10
11
12
13
14
finalIterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
while(selectionKeyIterator.hasNext()) {
    finalSelectionKey key = selectionKeyIterator.next();
 
    if(key.isAcceptable()) {
        acceptClientSocket(key, serverSocket);
    }elseif(key.isReadable()) {
        readRequest(key);
    }else{
        System.out.println("Invalid selection key");
    }
 
    selectionKeyIterator.remove();
}
  • line 13: Remember to remove theSelectionKey from the selected set  as theSelector does not do this for us, if we don’t do it, we will continue to process stale events.

The following code snippet demonstrates the use of  registration of aSocketChannel with the sameSelector that manages theServerSocketChannel. Here, however, the interest set is only for IO “read” events.

Registering a Channel with a Selector

1
2
3
finalSocketChannel client = serverSocket.accept();
client.configureBlocking(false);
client.register(key.selector(), SelectionKey.OP_READ);

3. Summary

In this beginners tutorial we understood some of the differences between IO and NIO and reasons for NIO’s existence and applicability. We have also covered the 4 main abstractions when working with NIO. Those are:

  • Buffers
  • Channels
  • Selectors
  • Charsets

We have seen how they can be used and how they work in tandem with each other.  With this tutorial in hand, you understand the basics of creating Channels and using them with Buffers. How to interact with Buffers and the rich API it provides for traversing buffer content. We have also learnt how to register Channels with Selectors and interact with theSelector via itsSelectionKey abstraction.

4. Working with Java NIO tutorial example source code

The source code contains the following examples:

  • Charset example.
  • FileChannel example. This example reads from a classpath resource filesrc/main/resources/file/input.txt and writes a String literal to a classpath resourcesrc/main/resources/file/output.txt. Be sure to check the foldertarget/classes/file when wanting to view the output of the write example.
  • Client Server example. Start the server first, then start the client. The client will attempt 10 connections to the server and write the same text 10 times to the server which will simply write the contents to console.

5. Download the source code

This was a Java Nio Tutorial for Beginners Example.

Download
You can download the full source code of this example here:Java Nio tutorial for beginners

Last updated on May 27th, 2020

Do you want to know how to develop your skillset to become aJava Rockstar?
Subscribe to our newsletter to start Rockingright now!
To get you started we give you our best selling eBooks forFREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to theTerms andPrivacy Policy

Thank you!

We will contact you soon.

Photo of JJJJMay 5th, 2017Last Updated: March 16th, 2021
0 343 8 minutes read
Photo of JJ

JJ

Jean-Jay Vester graduated from the Cape Peninsula University of Technology, Cape Town, in 2001 and has spent most of his career developing Java backend systems for small to large sized companies both sides of the equator.He has an abundance of experience and knowledge in many varied Java frameworks and has also acquired some systems knowledge along the way.Recently he has started developing his JavaScript skill set specifically targeting Angularjs and also bridged that skill to the backend with Nodejs.
Subscribe
Notify of
guest
I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.

I agree to theTerms andPrivacy Policy
The comment form collects your name, email and content to allow us keep track of the comments placed on the website. Please read and accept our website Terms and Privacy Policy to post a comment.