Understanding Java NIO: Core Components and How They Differ from Traditional IO
This article explains Java NIO introduced in Java 1.4, covering its core components—Channels, Buffers, and Selectors—detailing channel types, buffer operations, non‑blocking behavior, selector usage, and code examples such as FileChannel and Pipe, and compares NIO with classic IO.
Introduction
Java NIO was introduced in Java 1.4. Its core concepts are Channels, Buffers, and Selectors.
Core components
Channels
Buffers
Selectors
Channel and Buffer
All I/O starts with a Channel. Data is read from a Channel into a Buffer or written from a Buffer to a Channel.
Channel implementations
FileChannel – reads/writes files
DatagramChannel – UDP network I/O
SocketChannel – TCP network I/O
ServerSocketChannel – listens for incoming TCP connections and creates a SocketChannel for each
Buffer types
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
Selector
A Selector allows a single thread to monitor multiple Channels. Register Channels with a Selector and call select(), which blocks until at least one registered Channel is ready.
Java NIO vs. IO
IO is stream‑oriented and blocking; NIO is buffer‑oriented and can be non‑blocking. Selectors exist only in NIO.
Stream vs. Buffer – Classic IO processes bytes directly from a stream without intermediate storage and cannot move backwards. NIO reads data into a Buffer that can be repositioned, giving more flexible processing at the cost of explicit buffer management.
Blocking vs. Non‑blocking – In classic IO a thread calling read() or write() blocks until the operation completes. In NIO a non‑blocking call returns immediately if no data is available, allowing the thread to handle other Channels.
Use NIO when managing thousands of low‑traffic connections; classic IO may be preferable for a few high‑bandwidth connections.
Channel details
Channels are bidirectional, can be asynchronous, and always interact with a Buffer.
FileChannel example
public class FileChannelTest {
public static void main(String[] args) {
try {
RandomAccessFile aFile = new RandomAccessFile("1.txt", "rw");
FileChannel channel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = channel.read(buf);
while (bytesRead != -1) {
buf.flip();
while (buf.hasRemaining()) {
System.out.print((char) buf.get());
}
buf.clear();
bytesRead = channel.read(buf);
}
aFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}The call to buf.flip() switches the Buffer from write mode to read mode.
Buffer operations
Typical workflow: write → flip() → read → clear() or compact().
Write data into the Buffer
Call flip() to switch to read mode
Read data from the Buffer
Call clear() or compact() to prepare for the next write
Key properties:
capacity – total size of the Buffer
position – index for the next read or write
limit – in write mode, the maximum amount that can be written (equals capacity); in read mode, the amount of data available to read (set to the previous position)
Allocation example: ByteBuffer buf = ByteBuffer.allocate(48); Writing to a Buffer can be done via a Channel read ( int bytesRead = inChannel.read(buf);) or buf.put(...). flip() must be called once before reading; calling it multiple times can cause errors.
Reading can be performed by a Channel write ( int bytesWritten = outChannel.write(buf);) or byte b = buf.get();. rewind() resets position to 0 while keeping limit unchanged. clear() resets position to 0 and sets limit to capacity, discarding all data. compact() moves any unread data to the beginning of the Buffer and prepares the remaining space for writing without losing the unread bytes. mark() and reset() let you remember a specific position and later return to it.
Scatter/Gather
Scatter reads distribute data from a Channel into multiple Buffers; gather writes combine data from multiple Buffers into a single Channel.
Channel‑to‑Channel data transfer
If either source or target is a FileChannel, data can be transferred directly:
toChannel.transferFrom(position, count, fromChannel); positionis the start offset in the target file; count is the maximum number of bytes to transfer. For SocketChannel, only currently available data may be transferred.
Selector usage
Create a Selector: Selector selector = Selector.open(); Configure a Channel to non‑blocking and register it:
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);The second argument is the interest set, indicating which events (CONNECT, ACCEPT, READ, WRITE) you want to be notified about.
Selection methods: select() – blocks until at least one registered Channel is ready select(long timeout) – blocks up to the given timeout selectNow() – non‑blocking, returns immediately
After select() returns, retrieve the ready keys and iterate:
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// handle accept
} else if (key.isConnectable()) {
// handle connect
} else if (key.isReadable()) {
// handle read
} else if (key.isWritable()) {
// handle write
}
keyIterator.remove();
} Selector.wakeup()forces a blocked select() to return immediately. selector.close() invalidates all registered SelectionKeys but does not close the Channels.
Pipe
A Pipe provides a one‑way data connection between two threads, consisting of a source Channel and a sink Channel.
Create a Pipe: Pipe pipe = Pipe.open(); Write to the sink:
Pipe.SinkChannel sinkChannel = pipe.sink();
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while (buf.hasRemaining()) {
sinkChannel.write(buf);
}Read from the source:
Pipe.SourceChannel sourceChannel = pipe.source();
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = sourceChannel.read(buf);Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ZhiKe AI
We dissect AI-era technologies, tools, and trends with a hardcore perspective. Focused on large models, agents, MCP, function calling, and hands‑on AI development. No fluff, no hype—only actionable insights, source code, and practical ideas. Get a daily dose of intelligence to simplify tech and make efficiency tangible.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
