理解NIO中的Selector

Selector是选择器是NIO技术中的核心组件,可以将通道注册进选择器中,其主要作用就是使用一个线程来对多个通道中的已就绪通道进行选择,然后就可以对选择的通道进行数据处理,属于一对多的关系。这种机制在NIO技术中心称为“IO多路复用”。其优势是可以节省CPU资源

说的简单点就是一个线程通过选择器可以连接多个通道,从而完成高效的I/O

在Selector中有三个核心类:

Selector:主操作类,通过静态实例化,select()方法来管理已经注册的通道

SelectionKey:注册完通道之后返回的键,通过该类来描述通道的状态

SelectableChannel:通道,通过该类获取Socket对象,将之注册到Selector中

其关系是只用SelectableChannel通道对象才能被Selector.java选择器所复用因为只有SelectableChannel通道对象才有register(Selector sel,int ops)方法

 

SelectableChannel:

public abstract class SelectableChannel
    extends AbstractInterruptibleChannel
    implements Channel

其最常用的方法如下:

    public abstract SelectionKey register(Selector sel, int ops, Object att)
        throws ClosedChannelException;

把SelectableChannel对象注册到Selector上,第二个参数表明对该Channel的那个行为有兴趣可监听四中类型的事件:

  • Connect:某个Channel成功连接到另一个服务器称为“ 连接就绪”,对应常量SelectionKey.OP_CONNECT
  • Accept:一个Server Socket Channel准备好接收新进入的连接称为“ 接收就绪 ”,对应常量:SelectionKey.OP_ACCEPT
  • Read:一个有数据可读的通道可以说是“ 读就绪 ”,对应常量:SelectionKey.OP_READ
  • Write:等待写数据的通道可以说是“ 写就绪 ”,对应常量:SelectionKey.OP_WRITE

如果对多个事件有兴趣的话,用   |   来连接。

需要注意的是如果要把通道注册进选择器中,就必须让该通道非阻塞

channel.configureBlocking(false);

1.不能直接注销通道,需要通过SelectionKey.cancel()来进行注销

2.一个通道只能在任意选择器上注册一次

3.SelectableChannel在对象城下是安全的

4.新创建的SelectableChannel总是阻塞的,向选择器注册通道必须显式的让其变成非阻塞

5.应该是通过它来获取Socket对象!

      ServerSocketChannel open = ServerSocketChannel.open();
        ServerSocket socket = open.socket();
        socket.bind(new InetSocketAddress("localhost",8888));

SelectionKey

 

SelectionKey表示SelectableChannel在选择器中注册的标记。

注册一个通道,产生一个Key,调用通过某个Key的cancle()方法来关闭通道,关闭之后不会立即从selector中移除,而是添加到cancelledKeys中,在下一次select操作时移除,所以在调用某个key是,需要用isValid进行校验

 

包含两个操作集:

  • interest 集合:当前channel感兴趣的操作,此类操作将会在下一次选择器select操作时被交付,可以通过selectionKey.interestOps(int)进行修改.
  • ready 集合:表示此选择键上,已经就绪的操作.每次select时,选择器都会对ready集合进行更新;外部程序无法修改此集合

 

Selector

selector主要是作为SelectableChannel对象的多路复用器

创建:

Selector selector = Selector.open();
select方法
public abstract int select(long timeout)

具有阻塞性,其作用是选择一组键,其相应的通道以为I/O操作准备就绪。返回值是添加到就绪操作集的键的数目

selectedKeys方法

  public abstract Set<SelectionKey> selectedKeys();

返回一个已注册的selectedKey集合,

Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.
    } else if (key.isConnectable()) {
        // a connection was established with a remote server.
    } else if (key.isReadable()) {
        // a channel is ready for reading
    } else if (key.isWritable()) {
        // a channel is ready for writing
    }
    keyIterator.remove();
}

选择器执行选择的过程,系统底层会依次询问每个通道是否已经就绪,这个过程可能会造成调用线程进入阻塞状态,那么我们有以下两种方式可以唤醒在select()方法中阻塞的线程。

wakeup方法:

通过调用Selector对象的wakeup()方法让处在阻塞状态的select()方法立刻返回 
该方法使得选择器上的第一个还没有返回的选择操作立即返回。如果当前没有进行中的选择操作,那么下一次对select()方法的一次调用将立即返回。

close方法:

通过close方法关闭Selector, 该方法使得任何一个在选择操作中阻塞的线程都被唤醒,同时使得注册到该Selector的所有Channel被注销,所有的键将被取消,但是Channel本身并不会关闭。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java NIO Selector 可以用于多路复用 I/O,它可以同时监控多个 Channel 的 IO 状态,如读写就绪等,从而让你的程序可以同时处理多个网络连接。 使用 Selector 的基本流程如下: 1. 创建 Selector 对象:使用 `Selector.open()` 方法。 2. 创建并配置 Channel:每个 Channel 都必须注册到 Selector 上。 3. 向 Selector 注册感兴趣的事件:使用 `SelectionKey` 对象将 Channel 和感兴趣的事件绑定。 4. 通过 `select()` 方法监控 Channel:该方法会阻塞,直到至少有一个 Channel 处于就绪状态。 5. 处理就绪的 Channel:通过 `selectedKeys()` 方法获取所有就绪的 Channel,然后遍历每一个 Key,并根据 Key 的事件状态进行相应的处理。 6. 关闭 Selector:使用 `close()` 方法关闭 Selector。 以上就是 Selector 的基本使用方法。希望这些信息能帮助你理解和使用 Java NIO Selector。 ### 回答2: Java NIO(New Input/Output)提供了一种非阻塞I/O的能力,其selector是一种重要的组件。它允许程序通过一个单线程来监听多个通道上的事件并做出相应的处理。 使用Selector主要包括以下步骤: 1. 创建Selector实例: Selector selector = Selector.open(); 2. 创建Channel并设置为非阻塞模式: 在使用Selector之前,需要确保Channel处于非阻塞模式,例如SocketChannel或ServerSocketChannel: SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); 3. 将Channel注册到Selector上: 通过SelectionKey来表示Channel的注册状态,包括感兴趣的操作集合及其附加的数据。可以使用以下方法将Channel注册到Selector上: SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ); 4. 进行事件监听: 使用Selectorselect()方法进行事件监听,它会阻塞,直到有一个或多个事件发生: int readyChannels = selector.select(); if (readyChannels == 0) { continue; } 5. 获取已就绪的事件集合: 通过调用selector.selectedKeys()方法获取已经就绪的事件集合: Set<SelectionKey> selectedKeys = selector.selectedKeys(); 6. 遍历已就绪的事件集合并处理: 遍历selectedKeys集合,处理每一个就绪的事件: Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isReadable()) { // 可读事件处理逻辑 } if (key.isWritable()) { // 可写事件处理逻辑 } keyIterator.remove(); // 处理完毕后需要手动移除该事件,避免重复处理 } 7. 关闭Selector: 使用完Selector后需要及时关闭: selector.close(); 使用Selector可以实现多个通道的事件监听和处理,极大地提高了应用程序的性能和资源利用率。需要注意的是,在使用Selector时,一个线程可以管理多个Channel,但要谨慎处理每个Channel上的事件,以避免阻塞整个Selector处理线程。 ### 回答3: Java NIO(New I/O)是一种非阻塞I/O操作的Java API。它提供了一组用于高效处理I/O操作的类和接口。其SelectorNIO的核心组件之一,用于实现非阻塞I/O。 Selector是一个类似于调度员的对象,它可以同时监视多个通道的I/O事件。使用Selector可以实现单线程同时管理多个通道的I/O操作,提高了系统的效率。 使用Selector的主要步骤如下: 1. 创建一个Selector对象:通过调用Selector.open()方法创建一个Selector对象。 2. 将通道注册到Selector上:将需要监视的通道注册到Selector上,例如SocketChannel、ServerSocketChannel等。通过调用通道的register()方法完成注册。 3. 设置通道的非阻塞模式:通过调用通道的configureBlocking(false)方法将通道设置为非阻塞模式。 4. 选择通道:通过调用Selectorselect()方法选择通道,并返回已准备就绪的通道的数量。 5. 处理选择的通道:通过调用SelectorselectedKeys()方法获取选择的通道集合,可以通过遍历通道集合进行相应的读写操作。 6. 取消选择的通道:通过调用SelectionKey的cancel()方法取消选择的通道的注册。 示例代码如下: ```java Selector selector = Selector.open(); SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("example.com", 80)); socketChannel.register(selector, SelectionKey.OP_CONNECT); while (true) { int readyChannels = selector.select(); if (readyChannels == 0) { continue; } 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(); if (channel.isConnectionPending()) { channel.finishConnect(); } channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { // 处理读就绪的通道 SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); channel.read(buffer); buffer.flip(); // 处理读取到的数据 } keyIterator.remove(); } } ``` 以上是一个简单的Selector的使用示例,通过这些步骤,可以实现对多个通道的非阻塞I/O操作的监视和处理。需要注意的是,Selector是基于事件驱动的,可以实现高效的I/O操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值