Java NIO(New I/O)采用非阻塞I/O模型,通过通道(Channel)、缓冲区(Buffer)和选择器(Selector)实现高性能网络通信。与传统BIO相比,NIO减少了线程阻塞和上下文切换开销。
缓冲区(Buffer)工作原理 缓冲区是数据暂存的核心结构,底层通过java.nio.Buffer
类实现,关键属性包括:
capacity
: 缓冲区总容量position
: 当前读写位置limit
: 可操作数据边界mark
: 临时标记位置// 创建ByteBuffer的两种方式
ByteBuffer heapBuffer = ByteBuffer.allocate(1024); // 堆内存
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024); // 直接内存
// 读写操作示例
directBuffer.put((byte)1); // position+1
directBuffer.flip(); // limit=position, position=0
byte b = directBuffer.get(); // 读取数据
直接内存与堆内存对比 直接内存(DirectBuffer)通过Unsafe.allocateMemory
申请堆外内存,减少JVM堆与操作系统间的数据拷贝:
NIO通道分为文件通道和网络通道,底层通过虚引用机制管理资源。FileChannel
采用零拷贝技术提升性能:
// 文件零拷贝传输
try (FileChannel src = new FileInputStream("source.txt").getChannel();
FileChannel dest = new FileOutputStream("dest.txt").getChannel()) {
src.transferTo(0, src.size(), dest);
}
SocketChannel非阻塞模式 设置非阻塞模式后,读写操作立即返回,需配合选择器使用:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false); // 非阻塞模式
socketChannel.connect(new InetSocketAddress("example.com", 80));
// 非阻塞连接检查
while (!socketChannel.finishConnect()) {
// 处理其他任务
}
Selector通过epoll(Linux)或kqueue(Mac)实现多路复用,关键步骤:
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_READ);
while (true) {
int readyChannels = selector.select(); // 阻塞直到有事件
Set keys = selector.selectedKeys();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isReadable()) {
// 处理读事件
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(128);
channel.read(buffer);
buffer.flip();
}
iter.remove();
}
}
Linux epoll实现原理 NIO在Linux下通过epoll系统调用实现:
epoll_create
创建epoll实例epoll_ctl
注册文件描述符epoll_wait
等待I/O事件利用虚拟内存机制将文件直接映射到用户空间:
RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
MappedByteBuffer mappedBuffer = file.getChannel().map(
FileChannel.MapMode.READ_WRITE, 0, file.length());
// 直接操作内存数据
mappedBuffer.put(0, (byte)'X'); // 修改会同步到文件
底层mmap系统调用
非阻塞Echo服务器实现
public class NioEchoServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
client.read(buffer);
buffer.flip();
client.write(buffer);
}
iter.remove();
}
}
}
}
性能优化要点
通过理解这些底层机制,可以更高效地使用Java NIO构建高性能网络应用。实际开发中需结合业务场景选择合适的缓冲区大小、线程模型和IO策略。