面试题答案
一键面试-
选择器监控多个通道的原理:
- 选择器(Selector)是Java NIO中的核心组件,它能够使一个单独的线程管理多个通道(Channel)。
- 每个通道(例如SocketChannel等)可以注册到选择器上,并指定该通道希望Selector监控的事件类型,如连接就绪(
SelectionKey.OP_CONNECT
)、接受就绪(SelectionKey.OP_ACCEPT
)、读就绪(SelectionKey.OP_READ
)、写就绪(SelectionKey.OP_WRITE
)。 - 选择器通过调用
select()
方法阻塞等待,直到注册在其上的通道有感兴趣的事件发生。当有事件发生时,select()
方法返回,返回值表示有事件发生的通道数量。 - 然后可以通过
selectedKeys()
方法获取发生事件的SelectionKey
集合,遍历该集合处理对应的通道事件。
-
注册通道到选择器并监听特定事件的示例:
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 NIOExample {
public static void main(String[] args) {
try {
// 创建Selector
Selector selector = Selector.open();
// 创建ServerSocketChannel并设置为非阻塞模式
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
// 绑定端口
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
// 将ServerSocketChannel注册到Selector上,监听OP_ACCEPT事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 阻塞等待事件发生
int readyChannels = selector.select();
if (readyChannels == 0) continue;
// 获取发生事件的SelectionKey集合
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// 处理新连接
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
// 将新连接的SocketChannel注册到Selector上,监听OP_READ事件
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// 处理读事件
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
client.read(buffer);
buffer.flip();
System.out.println("Received: " + new String(buffer.array(), 0, buffer.limit()));
}
// 处理完事件后从集合中移除
keyIterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述示例中:
- 首先创建了一个
Selector
实例和一个ServerSocketChannel
,并将ServerSocketChannel
设置为非阻塞模式并绑定到8080端口。 - 然后将
ServerSocketChannel
注册到Selector
上,监听OP_ACCEPT
事件。 - 在循环中,通过
selector.select()
等待事件发生,当有事件发生时,获取selectedKeys
集合并遍历处理。如果是OP_ACCEPT
事件,接受新连接并将新的SocketChannel
注册到Selector
上监听OP_READ
事件;如果是OP_READ
事件,则读取客户端发送的数据。