Java NIO中的Selector(选择器)是一个用于检测多个非阻塞通道(Channel)是否准备就绪进行读写操作的关键组件,它实现了I/O多路复用技术。在单个线程中,Selector可以监听和管理多个Channel上的事件,这样就极大地提高了服务器端处理大量并发连接的效率。
原理: Selector基于操作系统提供的I/O复用API实现,例如Linux下的epoll或Windows下的IOCP等,它可以同时监控多个Channel,当任何一个Channel上有新的可读、可写或有连接建立等事件发生时,Selector会将这些事件通知给应用程序。
使用方法举例: 以下是一个简化的示例,展示如何创建一个Selector并注册Channel以及轮询待处理的事件:
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;public class SelectorExample {public static void main(String[] args) throws IOException {// 创建SelectorSelector selector = Selector.open();// 打开ServerSocketChannel,并设置为非阻塞模式ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.socket().bind(new InetSocketAddress(8080));// 将ServerSocketChannel注册到Selector上,监听接受新连接事件(OP_ACCEPT)SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {// 轮询等待感兴趣的事件发生if (selector.select() > 0) { // 如果至少有一个事件发生Iterator<SelectionKey> keysIterator = selector.selectedKeys().iterator();while (keysIterator.hasNext()) {SelectionKey selectedKey = keysIterator.next();// 检查是否有新的连接请求if (selectedKey.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) selectedKey.channel();SocketChannel client = server.accept();client.configureBlocking(false);// 注册新的SocketChannel到Selector上,监听读取数据事件(OP_READ)client.register(selector, SelectionKey.OP_READ);}// 检查是否有数据可读if (selectedKey.isReadable()) {SocketChannel channel = (SocketChannel) selectedKey.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int numRead = channel.read(buffer);if (numRead == -1) { // 表示客户端关闭了连接selectedKey.cancel();channel.close();} else if (numRead > 0) {// 处理接收到的数据...buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("Received: " + new String(data, StandardCharsets.UTF_8));// 数据读取完成后,可能需要重新注册读取事件// 或者调用clear()/compact()后再次注册buffer.clear();channel.register(selector, SelectionKey.OP_READ);}}// 移除已处理的事件键keysIterator.remove();}}}}
}
在这个例子中:
- 首先创建了一个Selector实例。
- 然后打开一个ServerSocketChannel并设置为非阻塞模式,将其绑定到特定端口以监听连接请求。
- 将ServerSocketChannel注册到Selector上,监听OP_ACCEPT事件,即新的连接请求。
- 在无限循环中,调用selector.select()阻塞等待事件发生。
- 当有事件发生时,通过迭代selectedKeys()来处理每个已触发的事件,包括接收新连接和从已有连接读取数据。
- 对于每一个读取事件,从对应的SocketChannel读取数据,并对数据进行相应处理。
- 事件处理完毕后,移除已处理的SelectionKey,准备下一轮事件轮询。
注意,实际应用中可能还需要考虑异常处理、数据完整性和线程安全等问题。