Channels & Buffers
TL;DR
- Channels and Buffers are the core components of Java NIO.
- A Channel represents an open connection to a hardware device, file, or network socket. It is bi-directional (can both read and write).
- A Buffer is a chunk of memory (an array) that you use to interact with the Channel. You read data from a channel into a buffer, or write data from a buffer into a channel.
Concept
In standard java.io, you interact directly with a Stream, asking it for one byte at a time.
In NIO, you do not interact with the Channel directly. The Channel is just a highway. The Buffer is the delivery truck.
The Buffer Lifecycle:
A ByteBuffer has three crucial pointers: capacity (total size), position (where we are currently writing/reading), and limit (how much valid data exists).
- Write to Buffer: You (or the Channel) write bytes into the Buffer. The
positionmoves forward. - Flip: You call
buffer.flip(). This resets thepositionback to 0, and sets thelimitto the amount of data written. The buffer is now ready to be read. - Read from Buffer: You (or the Channel) read the bytes out. The
positionmoves forward towards the limit. - Clear: You call
buffer.clear(). This resets the pointers, making the buffer ready to be overwritten with new data.
Examples
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelBufferDemo {
public static void main(String[] args) throws Exception {
// Open a RandomAccessFile, get its Channel
RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
FileChannel channel = file.getChannel();
// Allocate a Buffer of 48 bytes
ByteBuffer buffer = ByteBuffer.allocate(48);
// 1. Write to the Buffer (The channel reads from disk, writes to our buffer)
int bytesRead = channel.read(buffer);
// 2. FLIP! We are done writing to it, we want to read the data out.
buffer.flip();
// 3. Read from the Buffer
while(buffer.hasRemaining()) {
System.out.print((char) buffer.get()); // Read 1 byte at a time
}
// 4. CLEAR! Reset the buffer so the channel can write more data into it.
buffer.clear();
channel.close();
}
}
Interview Questions
Q: What is the difference between a Stream and a Channel?
A: 1. Directionality: Streams are inherently one-way (you have an InputStream for reading OR an OutputStream for writing). Channels are bi-directional; you can read and write to the same SocketChannel.
2. Blocking: Streams are always blocking. If no data is available on the network, stream.read() hangs the thread forever. Channels can be put into Non-Blocking mode, returning instantly if no data is available.
3. Interaction: You interact with Streams directly via byte arrays. You interact with Channels exclusively through Buffer objects.
Q: What is a “Direct Buffer” (ByteBuffer.allocateDirect())?
A: Normally, ByteBuffer.allocate() creates a byte array on the standard Java Heap. When performing Network I/O, the OS cannot read directly from the Java Heap (because the Garbage Collector might move the array mid-read!). The JVM has to copy the data from the Heap into OS memory first.
A Direct Buffer allocates memory natively outside the JVM Heap (directly in the OS memory space). The network card can read/write to it via Direct Memory Access (DMA) with zero JVM overhead. It is extremely fast for I/O, but very expensive to allocate and garbage collect.