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时需要注意文件描述符数的限制。