On this page
NIO Basics
Java NIO (New I/O, since Java 1.4) provides non-blocking I/O operations via buffers and channels — an alternative to the stream-based java.io package.
NIO vs Classic I/O
| Feature | java.io (Streams) | java.nio (Channels) |
|---|---|---|
| Data flow | Byte-by-byte | Buffer-based |
| Blocking | Always blocking | Blocking or non-blocking |
| Selectors | Not available | Multiplex channels |
| Best for | Simple file I/O | High-concurrency network I/O |
Buffers
A buffer is a container for data of a primitive type:
ByteBuffer buffer = ByteBuffer.allocate(1024); // heap buffer
buffer.put((byte) 'H');
buffer.put((byte) 'i');
buffer.flip(); // switch from write mode to read mode
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
Buffer Properties
| Property | Description |
|---|---|
capacity |
Total size |
position |
Current read/write index |
limit |
End of readable/writable data |
mark |
Saved position for reset |
Key methods: put(), get(), flip(), clear(), rewind(), compact().
Direct Buffers
ByteBuffer direct = ByteBuffer.allocateDirect(1024); // off-heap, faster for I/O
Direct buffers avoid copying between JVM heap and native memory — useful for network/file channels.
Channels
Channels transfer data to/from buffers:
try (RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
FileChannel channel = file.getChannel()) {
ByteBuffer buffer = ByteBuffer.allocate(256);
int bytesRead = channel.read(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
}
Common channel types:
| Channel | Purpose |
|---|---|
FileChannel |
File read/write |
SocketChannel |
TCP client |
ServerSocketChannel |
TCP server |
DatagramChannel |
UDP |
Non-Blocking SocketChannel
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("example.com", 80));
while (!channel.finishConnect()) {
// do other work while connecting
}
ByteBuffer buffer = ByteBuffer.allocate(256);
channel.read(buffer);
Selectors
A Selector multiplexes multiple channels — one thread handles many connections:
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // blocks until events occur
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (key.isAcceptable()) {
// accept new connection
} else if (key.isReadable()) {
// read data
}
}
}
This is the foundation of high-performance network servers (Netty builds on similar concepts).
Scatter/Gather I/O
Read into or write from multiple buffers in one operation:
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = { header, body };
channel.read(buffers); // scatter read
channel.write(buffers); // gather write
Best Practices
- Always call
flip()before reading from a buffer after writing - Use try-with-resources for channels
- Prefer NIO.2 (
java.nio.file) for file operations — simpler API - Use NIO channels + selectors for high-concurrency network servers
- Release direct buffers when no longer needed (GC handles this, but they are expensive to allocate)