面试题答案
一键面试- 注册通道到Selector:
- 首先,创建一个
Selector
实例,通过Selector.open()
方法。 - 然后,将
SelectableChannel
(如SocketChannel
或ServerSocketChannel
)设置为非阻塞模式,使用channel.configureBlocking(false)
。 - 接着,把通道注册到
Selector
上,并指定感兴趣的事件,例如:
- 首先,创建一个
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_CONNECT);
这里通过register
方法返回一个SelectionKey
,它代表了通道和Selector
之间的注册关系,并且包含了感兴趣的事件集合。
2. Selector轮询事件:
- 使用
selector.select()
方法,该方法会阻塞,直到至少有一个注册的通道上有感兴趣的事件发生。还有其他变体方法,如select(long timeout)
可以设置超时时间,selectNow()
不会阻塞,立即返回。 - 当
select()
返回时,表明有事件发生。可以通过selector.selectedKeys()
获取发生事件的SelectionKey
集合。
- 处理事件:
- 遍历
selectedKeys()
返回的集合,对每个SelectionKey
进行处理:
- 遍历
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isConnectable()) {
// 处理连接就绪事件
SocketChannel channel = (SocketChannel) key.channel();
// 完成连接操作
channel.finishConnect();
} else if (key.isReadable()) {
// 处理读就绪事件
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
buffer.flip();
// 处理读取到的数据
} else if (key.isWritable()) {
// 处理写就绪事件
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.wrap("data to write".getBytes());
channel.write(buffer);
}
keyIterator.remove();
}
- 在处理完事件后,需要从
selectedKeys
集合中移除当前处理的SelectionKey
,以避免重复处理。
通过以上流程,Selector
可以有效地识别并处理多个通道上发生的不同事件,实现高效的I/O多路复用。