Java NIO完整教程:通道、缓冲区和选择器实战详解

2024-06-21 李腾 129 次阅读 0 次点赞
Java NIO是Java 1.4引入的高性能I/O API,相比传统I/O具有非阻塞和异步操作优势。本文系统讲解NIO三大核心组件的工作原理,通过文件操作、网络服务器与客户端通信、缓冲区管理等实际代码示例,详细演示了NIO在高并发场景下的应用。内容涵盖从基础概念到实战应用的全流程,帮助开发者快速掌握NIO编程技巧,提升I/O处理效率。

Java NIO 是 Java 1.4 引入的新I/O API,提供了非阻塞、异步的I/O操作能力,相比传统的Java I/O API更高效。

核心组件

1、通道 (Channel) - 数据的传输通道

2、缓冲区 (Buffer) - 数据容器

3、选择器 (Selector) - 多路复用器,用于管理多个通道

示例代码

1. 文件读写示例

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOFileExample {
    public static void main(String[] args) {
        // 写入文件
        writeToFile("example.txt", "Hello, Java NIO!");
        
        // 读取文件
        readFromFile("example.txt");
    }
    
    // 写入文件
    public static void writeToFile(String filename, String content) {
        try (RandomAccessFile file = new RandomAccessFile(filename, "rw");
             FileChannel channel = file.getChannel()) {
            
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put(content.getBytes());
            buffer.flip(); // 切换为读模式
            
            while (buffer.hasRemaining()) {
                channel.write(buffer);
            }
            
            System.out.println("文件写入完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 读取文件
    public static void readFromFile(String filename) {
        try (RandomAccessFile file = new RandomAccessFile(filename, "r");
             FileChannel channel = file.getChannel()) {
            
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int bytesRead = channel.read(buffer);
            
            buffer.flip(); // 切换为读模式
            
            byte[] data = new byte[bytesRead];
            buffer.get(data);
            
            System.out.println("读取内容: " + new String(data));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. 网络服务器示例

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {
    private static final int PORT = 8080;
    
    public static void main(String[] args) throws IOException {
        // 创建选择器
        Selector selector = Selector.open();
        
        // 创建服务器通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);
        serverChannel.bind(new InetSocketAddress(PORT));
        
        // 注册到选择器,监听接受连接事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        System.out.println("服务器启动,监听端口: " + PORT);
        
        while (true) {
            // 阻塞直到有事件发生
            selector.select();
            
            // 获取就绪的事件
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iter = selectedKeys.iterator();
            
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                
                if (key.isAcceptable()) {
                    handleAccept(serverChannel, selector);
                }
                
                if (key.isReadable()) {
                    handleRead(key);
                }
                
                iter.remove();
            }
        }
    }
    
    private static void handleAccept(ServerSocketChannel serverChannel, Selector selector) 
            throws IOException {
        SocketChannel client = serverChannel.accept();
        client.configureBlocking(false);
        client.register(selector, SelectionKey.OP_READ);
        System.out.println("客户端连接: " + client.getRemoteAddress());
    }
    
    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        
        int bytesRead = client.read(buffer);
        
        if (bytesRead == -1) {
            client.close();
            System.out.println("客户端断开连接");
            return;
        }
        
        buffer.flip();
        byte[] data = new byte[buffer.remaining()];
        buffer.get(data);
        
        String message = new String(data);
        System.out.println("收到消息: " + message);
        
        // 回显消息
        ByteBuffer response = ByteBuffer.wrap(("服务器回复: " + message).getBytes());
        client.write(response);
    }
}

3. 网络客户端示例

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class NIOClient {
    public static void main(String[] args) throws IOException {
        SocketChannel client = SocketChannel.open();
        client.configureBlocking(false);
        client.connect(new InetSocketAddress("localhost", 8080));
        
        // 等待连接完成
        while (!client.finishConnect()) {
            System.out.println("正在连接服务器...");
        }
        
        System.out.println("连接服务器成功!");
        
        Scanner scanner = new Scanner(System.in);
        
        while (true) {
            System.out.print("请输入消息 (输入exit退出): ");
            String message = scanner.nextLine();
            
            if ("exit".equalsIgnoreCase(message)) {
                break;
            }
            
            // 发送消息
            ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
            client.write(buffer);
            
            // 接收响应
            ByteBuffer response = ByteBuffer.allocate(1024);
            int bytesRead = client.read(response);
            
            if (bytesRead > 0) {
                response.flip();
                byte[] data = new byte[response.remaining()];
                response.get(data);
                System.out.println("服务器响应: " + new String(data));
            }
        }
        
        client.close();
        scanner.close();
    }
}

4. Buffer 操作示例

import java.nio.ByteBuffer;

public class BufferExample {
    public static void main(String[] args) {
        // 创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);
        
        System.out.println("初始状态:");
        printBufferInfo(buffer);
        
        // 写入数据
        byte[] data = "Hello".getBytes();
        buffer.put(data);
        
        System.out.println("\n写入数据后:");
        printBufferInfo(buffer);
        
        // 切换为读模式
        buffer.flip();
        
        System.out.println("\nflip()后:");
        printBufferInfo(buffer);
        
        // 读取数据
        System.out.println("\n读取数据:");
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        System.out.println();
        
        System.out.println("\n读取完成后:");
        printBufferInfo(buffer);
        
        // 清空缓冲区
        buffer.clear();
        
        System.out.println("\nclear()后:");
        printBufferInfo(buffer);
    }
    
    private static void printBufferInfo(ByteBuffer buffer) {
        System.out.println("Position: " + buffer.position());
        System.out.println("Limit: " + buffer.limit());
        System.out.println("Capacity: " + buffer.capacity());
        System.out.println("Remaining: " + buffer.remaining());
    }
}

NIO 的优势

1、非阻塞 I/O - 线程不需要等待I/O操作完成

2、选择器机制 - 单个线程可以管理多个通道

3、内存映射文件 - 可以直接操作文件内存

4、更高效的缓冲区操作

使用场景

1、高并发网络服务器

2、大文件处理

3、需要高性能I/O的应用

4、实时数据处理系统

这些示例展示了Java NIO的基本用法,包括文件操作、网络通信和缓冲区管理。NIO在处理高并发I/O时比传统I/O更高效,但代码相对复杂一些。

最后更新于3月前
本文由人工编写,AI优化,转载请注明原文地址: Java NIO使用指南:核心组件详解与实战代码示例

评论 (1)

发表评论

昵称:加载中...
小草莓🍓2025-11-08 19:00:20
最近正好在优化项目的文件处理模块,用NIO改造后性能提升很明显。示例代码很实用,特别是Buffer的flip操作,之前在这里踩过坑。