Over

120,000

Worldwide

Saturday - Sunday CLOSED

Mon - Fri 8.00 - 18.00

Call us

 

NIO in Java

NIO in Java

Buffer, Channel, Selector

Java has introduced a new I/O system, which is called NIO (New I/O). This provides another way of working than the standard I/O we know. Java NIO is high-performance networking and file handling structure, which works as an alternative IO API for Java.

The java.io package was introduced from Java 1.0 and it provides several features: 1. InputStream and OutputStream, provide data one byte at a time

2. Reader and Writer, a convenience wrappers for the streams

3. Blocking mode, for waiting a complete message

And the java.nio package was introduced from Java 1.4 and it also provides several operations: 1. Buffer, for reading a chunks of data at a time

2. CharsetDecoder, to map raw bytes to/from readable characters

3. Channel, to communicate with the outside world

4. Selector, to enable multiplexing on a SelectableChannel and provide access to any Channels that are ready for I/O

5. Non-blocking mode, to read whatever is ready

The main difference between regular IO and NIO is non-blocking IO. The main benefit of non-blocking IO is that we need fewer threads to handle the same amount of IO requests. But when multiple calls are done using blocking IO, for each call a new thread is created. Another important difference between Java IO and NIO is that IO is a stream oriented, while NIO is buffer oriented. The data is read into a buffer from which it is later processed. You can move forward and back in the buffer as you need to do. Before using Java NIO we know that have two foremost reasons for it:

Non-blocking IO operation

Buffer oriented approach

Non-blocking IO operation

This means that it reads the data whichever is ready. For example, a thread can ask a channel to read the data from a buffer and the thread can go for other work during that time and continue again from the previous point where it has left. During that time, the reading operation is complete which increases the overall efficiency.

Buffer oriented approach

This allows us to move forward and back in the buffer as it is needed. The data is read into a buffer and cached there. Whenever the data is required, it is further processed from the buffer.

The main working of Java NIO package is based on some core components.

Buffer

Java NIO Buffers can be thought of as a simple object that acts as a fixed-size data container that can be used to write data to a channel or read data from a channel so that the buffers become the endpoints of the channels.

Buffers make the NIO package more efficient and faster than classic IO.

Java NIO buffers can be classified in several variants:

1. ByteBuffer

2. MappedByteBuffer

3. CharBuffer

4. DoubleBuffer

5. FloatBuffer

6. IntBuffer

7. LongBuffer

8. ShortBuffer

Here is an example of CharBuffer:

import java.nio.CharBuffer;

public class CharBufferExample {

public static void main(String[] args) {

CharBuffer charBuffer = CharBuffer.allocate(14);

String string = “buffer example”;

System.out.println(“Input: ” + string);

for (int i = 0; i < string.length(); i++) {

char c = string.charAt(i);

charBuffer.put(c);

}

int position = charBuffer.position();

System.out.println(“Data written to buffer – position: ” + position);

charBuffer.flip();

System.out.println(“Reading: “);

while (charBuffer.hasRemaining()) {

System.out.println(charBuffer.get());

}

charBuffer.position(7);

charBuffer.mark();

charBuffer.position(8);

charBuffer.reset();

System.out.println(“Restored position : ” + charBuffer.position());

}

}

The allocate(int capacity) method is used to allocate a new buffer with capacity as parameter, and whenever there is sent a negative integer it throws IllegalArgumentException.

With the put() method we write data in buffer and in case of read() method it writes data from channel into buffer.

The flip() method switches the mode of Buffer from writing to reading mode. It sets the position to 0, and sets the limit to where position was at time of writing.

The get() is a method of buffer which is used to read data from buffer.

The mark() method is used to mark any particular position in a buffer while reset() makes position back to marked position.

The output for the code above will be:

Input: buffer example

Data written to buffer – position: 14

Reading:

b

u

f

f

e

r

e

x

a

m

p

l

e

Restored position : 7

Another example of buffers can be IntBuffer.

import java.nio.IntBuffer;

import java.util.Arrays;

public class IntBufferExample {

public static void main(String[] args) {

int Capacity = 10;

IntBuffer intBuffer = IntBuffer.allocate(Capacity);

intBuffer.put(12);

intBuffer.put(3, 17);

System.out.println(“The IntBuffer is: ” + Arrays.toString(intBuffer.array())); intBuffer.put(5);

intBuffer.rewind();

System.out.println(“The IntBuffer is: ” + Arrays.toString(intBuffer.array())); int value1 = intBuffer.get();

System.out.println(“The value at current position of IntBuffer is: ” + value1); int value2 = intBuffer.get();

System.out.println(“The value at current position of IntBuffer is: ” + value2); }

}

The allocate(int capacity) requires a single parameter, the capacity of the buffer. It returns the new IntBuffer that is allocated. If the capacity provided is negative, then the IllegalArgumentException is thrown.

The put(int i) method is used to write the given int into the newly created int buffer at the current position, and then increments the position.

With the array() method we return the int array that backs this buffer. The rewind() method is used to rewind the buffer. The position is set to zero and the mark is discarded. And with the get() method we read the int at the given buffer’s current position, and then increment the position.

The output for this code will be:

The IntBuffer is: [12, 0, 0, 17, 0, 0, 0, 0, 0, 0]

The IntBuffer is: [12, 5, 0, 17, 0, 0, 0, 0, 0, 0]

The value at current position of IntBuffer is: 12

The value at current position of IntBuffer is: 5

Let’s look at another example, which has the same methods, we have used above. This is an example of a DoubleBuffer.

import java.nio.BufferUnderflowException;

import java.nio.DoubleBuffer;

import java.nio.ReadOnlyBufferException;

import java.util.Arrays;

public class DoubleBufferExample {

public static void main(String[] args) {

int capacity = 5;

try {

DoubleBuffer fb = DoubleBuffer.allocate(capacity);

fb.put(8.21D);

fb.put(9.45D);

fb.put(1.16D);

fb.rewind();

System.out.println(“The DoubleBuffer: ” + Arrays.toString(fb.array()));

double value1 = fb.get();

System.out.println(“Double Value: ” + value1);

double value2 = fb.get();

System.out.println(“Next double Value: ” + value2);

} catch (IllegalArgumentException e) {

System.out.println(“IllegalArgumentException caught”);

} catch (ReadOnlyBufferException e) {

System.out.println(“ReadOnlyBufferException caught”);

} catch (BufferUnderflowException e) {

System.out.println(“Exception throws: ” + e);

}

}

}

The result for this will be:

The DoubleBuffer: [8.21, 9.45, 1.16, 0.0, 0.0]

Double Value: 8.21

Next double Value: 9.45

Another example can be the MappedByteBuffer.

import java.nio.*;

import java.nio.channels.FileChannel;

import java.io.*;

public class MappedByteBufferExample {

public static void main(String[] args) throws IOException {

File file = new File(“C://Users//Comp//fileTest.txt”);

FileInputStream fin = new FileInputStream(file);

FileChannel fc = fin.getChannel();

MappedByteBuffer mapByteBuff = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.length());

while (mapByteBuff.hasRemaining()) {

System.out.print((char) mapByteBuff.get());

}

System.out.println();

if (mapByteBuff.isReadOnly()) {

System.out.print(“MappedByteBuffer is read only”);

} else {

System.out.print(“MappedByteBuffer is not read only”);

}

}

}

The result is the content of the .txt file and the line that it’s a read only.

This is an example file for MappedByteBuffer.

MappedByteBuffer is read only

Channel

The Channels are a new primitive I/O abstraction. A Channel is a bit like a stream used to communicate with the outside world. From the channel we can read the data into a buffer or write from a buffer. The Channels are available for non-blocking I/O operations. Here is more clearly seen the interaction of the channel and buffer:

The Channel can be implemented in several classes

1. FileChannel – for reading data from a file.

2. DatagramChannel – to read and write the data over the network via User Datagram Protocol. 3. SocketChannel – can read and write the data over the network via Transmission Control Protocol. 4. ServerSocketChannel – to read and write the data over TCP connections, same as a web server.

Here is an example of Channel, using FileChannel.

import java.io.IOException;

import java.io.RandomAccessFile;

import java.nio.ByteBuffer;

import java.nio.channels.FileChannel;

public class ChannelExample {

public static void main(String[] args) throws IOException {

RandomAccessFile accessFile = new

RandomAccessFile(“C:/Users/Comp/IdeaProjects/JavaLessons/src/com/company/nio/text.txt”, “r”); FileChannel fileChannel = accessFile.getChannel();

ByteBuffer byteBuffer = ByteBuffer.allocate(512);

while (fileChannel.read(byteBuffer) > 0) {

byteBuffer.flip();

while (byteBuffer.hasRemaining()) {

System.out.print((char) byteBuffer.get());

}

}

accessFile.close();

}

}

This will output the content of the .txt file. The output will be:

This is an example

Selector

A selector is an object which monitors multiple channels for the events, and this is available for non blocking I/O operations. The selector is used to select the channels which are ready for the I/O operation. Here is an image for more clarity:

We can get selector instance by calling its static method open(). After open selector we should register a non-blocking mode channel with it which returns an instance of SelectionKey. SelectionKey is a

collection of operations that can be performed with channel or with the help of SelectionKey we can know the state of channel.

The main operations of state of channel represented by selection key are several: 1. SelectionKey.OP_CONNECT − which is ready to connect to server.

2. SelectionKey.OP_ACCEPT − which is ready to accept incoming connections. 3. SelectionKey.OP_READ − which is ready to data read.

4. SelectionKey.OP_WRITE − which is ready to data write.

Selector selector = Selector.open();

channel.configureBlocking(false);

SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

while (true) {

int readyChannels = selector.selectNow();

if (readyChannels == 0) continue;

Set<SelectionKey> selectedKeys = selector.selectedKeys();

Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

while (keyIterator.hasNext()) {

SelectionKey selectionKey = keyIterator.next();

if (selectionKey.isAcceptable()) {

} else if (selectionKey.isConnectable()) {

} else if (selectionKey.isReadable()) {

} else if (selectionKey.isWritable()) {

}

keyIterator.remove();

}

}

The code above shows a Selector opening, registers a channel with it and after it monitors the Selector for four events, which are accept, connect, read, and write.

1. isReadable() – states that weather key’s channel is ready for read or not.

2. isWritable() – states that weather key’s channel is ready for write or not.

3. isAcceptable() – states that weather key’s channel is ready for accepting incoming connection or not.

4. isConnectable() – tests weather this key’s channel has either finished, or failed to finish, its socket-connection operation.

By calling the select static method, we select a channel from selector. And this method is overloaded:

1. select() − blocks the current thread until at least one channel is ready for the events it is registered for.

2. select(long timeout) − does the same as select() except it blocks the thread for a maximum of timeout milliseconds (the parameter).

3. selectNow() − doesn’t block at all, returns immediately with whatever channels are ready.

So you have been introduced to new I/O in java, which has more advantages and you can choose whenever it is needed to use for reading blocks of data from disk, rather than byte by byte. This new I/O offers you new concepts of working through buffers, selector, file mapping and others.

Leave a Reply

Your email address will not be published. Required fields are marked *

Working Hours

  • Monday 9am - 6pm
  • Tuesday 9am - 6pm
  • Wednesday 9am - 6pm
  • Thursday 9am - 6pm
  • Friday 9am - 6pm
  • Saturday Closed
  • Sunday Closed