5.NIO的Selector(选择器)

Java NIO的Selector允许单线程处理多个Channel,减少了线程上下文切换的开销。创建Selector通过Selector.open(),Channel需是非阻塞的并使用configureBlocking()设置。Selector关注connect、accept、read、write事件,当事件就绪,可以通过SelectionKey检查并获取已准备就绪的通道。Selector.select()方法用于选择感兴趣的事件就绪的通道。

Selector 一般称 为选择器 ,它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。

使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。

                          

Selector 的使用:

  • 创建-----通过调用Selector.open()方法创建一个Selector对象
  • 注册-----注册Channel到Selector。Channel必须是非阻塞的。channel可以是SocketChannel,也可以是SocketChannel。SelectableChannel抽象类configureBlocking() 方法是由 AbstractSelectableChannel抽象类实现的,SocketChannel、ServerSocketChannel、DatagramChannel都是直接继承了 AbstractSelectableChannel抽象类 。 
    channel.configureBlocking(false);
    SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
  • 事件-----“ interest集合 ”,意思是在通过Selector监听Channel时对什么事件感兴趣。【connect+accept+read+write】。通道触发了一个事件意思是该事件已经就绪。比如某个Channel成功连接到另一个服务器称为“ 连接就绪 ”。一个Server Socket Channel准备好接收新进入的连接称为“ 接收就绪 ”。一个有数据可读的通道可以说是“ 读就绪 ”。等待写数据的通道可以说是“ 写就绪 ”。
  • 使用1-----一个SelectionKey对象表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
    key.attachment(); //返回SelectionKey的attachment,attachment可以在注册channel的时候指定。
    key.channel(); // 返回该SelectionKey对应的channel。
    key.selector(); // 返回该SelectionKey对应的Selector。
    key.interestOps(); //返回代表需要Selector监控的IO操作的bit mask
    key.readyOps(); // 返回一个bit mask,代表在相应channel上可以进行的IO操作。
  • 使用2-----检查事件是否就绪。
    key.isAcceptable();//是否可读,是返回 true
    boolean isWritable()://是否可写,是返回 true
    boolean isConnectable()://是否可连接,是返回 true
    boolean isAcceptable()://是否可接收,是返回 true
  • 使用3-----通过selector获取通道。通过Selector的select()方法可以选择已经准备就绪的通道 (这些通道包含你感兴趣的的事件)。比如你对读就绪的通道感兴趣,那么select()方法就会返回读事件已经就绪的那些通道。
    Selector几个重载的select()方法:
    
    int select():阻塞到至少有一个通道在你注册的事件上就绪了。
    int select(long timeout):和select()一样,但最长阻塞时间为timeout毫秒。
    int selectNow():非阻塞,只要有通道就绪就立刻返回。
    
    
    select()方法返回的int值表示有多少通道已经就绪,是自上次调用select()方法后有多少通道变成就绪状态。之前在select()调用时进入就绪的通道不会在本次调用中被记入,而在前一次select()调用进入就绪但现在已经不在处于就绪的通道也不会被记入。例如:首次调用select()方法,如果有一个通道变成就绪状态,返回了1,若再次调用select()方法,如果另一个通道就绪了,它会再次返回1。如果对第一个就绪的channel没有做任何操作,现在就有两个就绪的通道,但在每次select()方法调用之间,只有一个通道就绪了。
    
    一旦调用select()方法,并且返回值不为0时,则 可以通过调用Selector的selectedKeys()方法来访问已选择键集合 。如下: 
    Set selectedKeys=selector.selectedKeys(); 
    进而可以放到和某SelectionKey关联的Selector和Channel。如下所示:
    
    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, 
    该方法使得任何一个在选择操作中阻塞的线程都被唤醒(类似wakeup()),同时使得注册到该Selector的所有Channel被注销,所有的键将被取消,但是Channel本身并不会关闭。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良之才-小良

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值