Comprehensive Guide to Java NIO: Concepts, Core Components, and Code Examples
This article provides an in‑depth tutorial on Java NIO, covering its fundamentals, IO models (BIO, NIO, AIO), core components such as Channel, Buffer and Selector, detailed code examples for a server and client, and a summary of best practices and limitations.
Java NIO (New I/O) is an asynchronous, event‑driven network programming framework built on top of the NIO client/server APIs. It solves the concurrency problems of traditional blocking I/O (BIO) by using the Reactor pattern and multiplexed selectors.
1. Network Programming Basics
Socket represents a bidirectional communication endpoint; in Linux it is treated as a special file, allowing read/write operations similar to file I/O. The typical TCP handshake involves listen, accept, connect, and close calls.
2. IO Models
Five IO models are described: Blocking I/O (BIO), Non‑blocking I/O (NIO), Multiplexed I/O, Signal‑driven I/O, and Asynchronous I/O (AIO). BIO blocks the thread until the operation completes, while NIO (synchronous non‑blocking) allows a single thread to manage many connections via a selector. AIO performs both read and write asynchronously, notifying the application via callbacks.
3. NIO Core Components
Channel
All NIO operations start from a Channel, which works with a Buffer for reading and writing. Common implementations are SocketChannel, ServerSocketChannel, and FileChannel.
Buffer
A buffer is a memory block that holds data. Important properties are capacity, limit, position, and mark. Typical operations include allocate, wrap, allocateDirect, put, get, flip, clear, rewind, and reset.
Selector
The selector is the multiplexing engine that monitors multiple channels for events such as OP_ACCEPT, OP_CONNECT, OP_READ, and OP_WRITE. Channels must be configured as non‑blocking before registration.
4. Code Example – NIO Server
public class NIOServer {
private static Selector selector;
public static void main(String[] args) {
init();
listen();
}
private static void init() {
ServerSocketChannel serverSocketChannel = null;
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(9000));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NioServer 启动完成");
} catch (IOException e) { e.printStackTrace(); }
}
private static void listen() {
while (true) {
try {
selector.select();
Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();
while (keysIterator.hasNext()) {
SelectionKey key = keysIterator.next();
keysIterator.remove();
handleRequest(key);
}
} catch (Throwable t) { t.printStackTrace(); }
}
}
private static void handleRequest(SelectionKey key) throws IOException {
SocketChannel channel = null;
try {
if (key.isAcceptable()) {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
channel = serverSocketChannel.accept();
channel.configureBlocking(false);
System.out.println("接受新的 Channel");
channel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int count = channel.read(buffer);
if (count > 0) {
System.out.println("服务端接收请求:" + new String(buffer.array(), 0, count));
channel.register(selector, SelectionKey.OP_WRITE);
}
}
if (key.isWritable()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("收到".getBytes());
buffer.flip();
channel = (SocketChannel) key.channel();
channel.write(buffer);
channel.register(selector, SelectionKey.OP_READ);
}
} catch (Throwable t) {
t.printStackTrace();
if (channel != null) channel.close();
}
}
}5. Code Example – NIO Client
public class NIOClient {
public static void main(String[] args) {
new Worker().start();
}
static class Worker extends Thread {
@Override
public void run() {
SocketChannel channel = null;
Selector selector = null;
try {
channel = SocketChannel.open();
channel.configureBlocking(false);
selector = Selector.open();
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(new InetSocketAddress(9000));
while (true) {
selector.select();
Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();
while (keysIterator.hasNext()) {
SelectionKey key = keysIterator.next();
keysIterator.remove();
if (key.isConnectable()) {
channel = (SocketChannel) key.channel();
if (channel.isConnectionPending()) {
channel.finishConnect();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("你好".getBytes());
buffer.flip();
channel.write(buffer);
}
channel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = channel.read(buffer);
if (len > 0) {
System.out.println("[" + Thread.currentThread().getName() + "]收到响应:" + new String(buffer.array(), 0, len));
Thread.sleep(5000);
channel.register(selector, SelectionKey.OP_WRITE);
}
}
if (key.isWritable()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("你好".getBytes());
buffer.flip();
channel = (SocketChannel) key.channel();
channel.write(buffer);
channel.register(selector, SelectionKey.OP_READ);
}
}
}
} catch (Exception e) { e.printStackTrace(); }
finally {
try { if (channel != null) channel.close(); } catch (IOException e) { e.printStackTrace(); }
try { if (selector != null) selector.close(); } catch (IOException e) { e.printStackTrace(); }
}
}
}
}6. Summary and Best Practices
The development workflow for an NIO server includes creating a ServerSocketChannel, configuring it as non‑blocking, registering it with a Selector for OP_ACCEPT, looping on selector.select(), handling accept, read, and write events, and managing buffers and channel states appropriately.
Key take‑aways:
Understand the core NIO classes: Selector, SocketChannel, ServerSocketChannel, FileChannel, ByteBuffer, and SelectionKey.
Be aware of TCP issues such as packet fragmentation, sticky packets, and connection management.
Handle resource cleanup carefully: close channels and selectors to avoid leaks.
Recognize the limitations of raw NIO (complexity, platform bugs) and consider higher‑level frameworks like Netty for production systems.
Overall, mastering Java NIO provides a solid foundation for building high‑performance, scalable network applications.
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.
Wukong Talks Architecture
Explaining distributed systems and architecture through stories. Author of the "JVM Performance Tuning in Practice" column, open-source author of "Spring Cloud in Practice PassJava", and independently developed a PMP practice quiz mini-program.
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.
