Java NIO Selector详解

Java NIO中的Selector是一个可选择通道的多路复用器,它可以同时监控多个通道的IO事件(比如连接请求、数据到达等),并且只会选择处于就绪状态的通道进行处理。

Selector的主要作用是解决单线程无法同时处理多个客户端连接的问题,通过一个线程使用Selector来监控多个通道,从而实现了单线程同时处理多个IO操作。

以下是一个简单的使用Selector的例子:

try {
    // 创建Selector
    Selector selector = Selector.open();

    // 打开ServerSocketChannel
    ServerSocketChannel serverSocket = ServerSocketChannel.open();
    serverSocket.bind(new InetSocketAddress("localhost", 8080));
    serverSocket.configureBlocking(false);

    // 将ServerSocketChannel注册到Selector,并指定只对接收连接事件感兴趣
    serverSocket.register(selector, SelectionKey.OP_ACCEPT);

    while (true) {
        // 阻塞等待就绪的通道
        int numReady = selector.select();

        // 处理就绪的通道
        if (numReady > 0) {
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectedKeys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                if (key.isAcceptable()) {
                    // 处理连接请求
                    SocketChannel socket = serverSocket.accept();
                    socket.configureBlocking(false);
                    socket.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    // 处理数据到达事件
                    SocketChannel socket = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    socket.read(buffer);
                    buffer.flip();
                    String msg = Charset.forName("UTF-8").decode(buffer).toString();
                    System.out.println("received msg: " + msg);
                }
                it.remove();
            }
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

这段代码使用Selector来实现一个简单的TCP服务器,它监听8080端口的连接请求,并在有新的连接请求时将对应的SocketChannel注册到Selector中,然后通过Selector监听SocketChannel的读事件,在有数据到达时读取数据并输出。

需要注意的是,Selector只能用于非阻塞通道(比如SocketChannel、ServerSocketChannel等),如果要使用阻塞式通道(比如FileChannel),则必须将其注册到Selector中的一个专门用于阻塞式通道的线程上。此外,由于Selector采用了epoll机制,它对打开的文件描述符数有限制,在Linux系统下一般为1024,因此使用Selector时需要注意文件描述符数的限制。