《Java NIO》学习笔记四 选择器(Selector)

4 篇文章 0 订阅

一、选择器基础

选择器(Selector):

选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态。

可选择通道(SelectorChannel):

这个抽象类提供了实现通道的可选择性所需要的公共方法。SelectableChannel可以被注册到Selector对象上,同时可以指定对那

个选择器而言,那种操作是感兴趣的。所有socket通道都是可选择的。

选择键(SelectionKey):

选择键封装了特定的通道与特定的选择器的注册关系。选择键对象被SelectableChannel.register( ) 返回并提供一个表示这种注册关系的标记。

 

 

 

 

二、SelectorChannel的相关API的方法:

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

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

public abstract boolean isRegistered( );            //调用isRegistered( )方法来检查一个通道是否被注册到任何一个选择器上

public abstract SelectionKey keyFor (Selector sel);  //调用keyFor( )方法将返回与该通道和指定的选择器相关的键

public abstract int validOps( );                  //通过调用validOps( )方法来获取特定的通道所支持的操作集合。

public abstract void configureBlocking (boolean block)throws IOException;

public abstract boolean isBlocking( );

public abstract Object blockingLock( );

 

SelectableChannel类的一个register( )方法的重载版本接受一个Object类型的参数。这是一个方便您在注册时附加一个对象到新生成的键上的方法。以下代码:

SelectionKey key =channel.register (selector, SelectionKey.OP_READ, myObject);

等价于:

SelectionKey key = channel.register (selector, SelectionKey.OP_READ);

key.attach (myObject);

 

三、SelectionKey类的相关的API的方法:

public static final int OP_READ   //

public static final int OP_WRITE  //

public static final int OP_CONNECT //连接 

public static final int OP_ACCEPT  //接受

一个选择键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。channel()selector()方法反映了这种关系。

public abstract SelectableChannel channel( );  //调用channel( )方法返回与该键相关的SelectableChannel对象

public abstract Selector selector( );          //调用selector( )则返回相关的Selector对象

public abstract void cancel( );     //键对象表示了一种特定的注册关系。当应该终结这种关系的时候,可以调用cancel( )方法

public abstract boolean isValid( );  //通过调用isValid( )方法来检查它是否仍然表示一种有效的关系

public abstract int interestOps( );  //通过调用interestOps( )方法来获取那些通道/选择器组合体所感兴趣的操作

public abstract void interestOps (int ops); //修改通道感兴趣的操作

public abstract int readyOps( );   //通过调用键的readyOps( )方法来获取相关的通道的已经就绪的操作

public final Object attach (Object ob) //调用attach( )方法将在键对象中保存所提供的对象的引用

public final Object attachment( )    //调用attachment( )方法来获取与键关联的附件句柄。例如缓冲区。

 

四、Selector类的相关API的方法:

public static Selector open( ) throws IOException    //创建一个选择器

public abstract boolean isOpen( );

public abstract void close( ) throws IOException;

public abstract SelectionProvider provider( );

public abstract int select( ) throws IOException;

public abstract int select (long timeout) throws IOException;

public abstract int selectNow( ) throws IOException;

public abstract void wakeup( );

public abstract Set keys( );        //获取与选择器关联的已经注册的键的集合

public abstract Set selectedKeys( ); //获取已选择的键的集合,这是一个键的集合,每个键都关联一个已经准备好至少一种操作的通道

 

Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();  //获取准备好的信道所关联的Key集合的iterator实例 

 

1Selector类的select( )方法有以下三种不同的形式:

1第一种是最简单的没有参数的形式:

Selector类的select()方法会无限阻塞等待,直到有信道准备好了IO操作,或另一个线程唤醒了它(调用了该选择器的wakeup())。

2第二种是带有超时参数的调用形式:

有时您会想要限制线程等待通道就绪的时间。这种情况下,可以使用这种形式:

如果在您提供的超时时间(以毫秒计算)内没有通道就绪时,它将返回0。将超时参数指定为0表示将无限期等待,那么它就在各个方面都等同于使用无参数版本的select( )了。

3第三种也是最后一种形式是完全非阻塞的:

int n = selector.selectNow( );

selectNow()方法执行就绪检查过程,但不阻塞。如果当前没有通道就绪,它将立即返回0

 

2selector()方法的作用及Selector实例内部工作原理:

Select()方法返回的是自上次调用它之后,有多少通道变为就绪状态。如果调用select()方法,因为有一个通道变成就绪状态,返回了1,若再次调用select()方法,如果另一个通道就绪了,它会再次返回1。如果对第一个就绪的channel没有做任何操作,现在就有两个就绪的通道,但在每次select()方法调用之间,只有一个通道就绪了。

 

Selector实例的作用是监控一组信道的I/O状态,它就类似一个观察者,只要我们把需要探知的SocketChannel告诉Selector,我们接着做别的事情(非阻塞IO特性),当有事件(比如,连接打开、数据到达等)发生时,它会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的SocketChannel,然后,我们从这个Channel中读取数据,接着我们可以处理这些数据。

 

Selector内部原理实际是对所注册的Channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个Channel有所注册的事情发生,比如数据来了,它就会读取Channel中的数据,并对其进行处理。

 

3为了建立监控三个Socket通道的选择器,您需要做像这样的事情

Selector selector = Selector.open( );

channel1.register (selector, SelectionKey.OP_READ);

channel2.register (selector, SelectionKey.OP_WRITE);

channel3.register (selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

readyCount = selector.select (10000); 

 

4停止选择过程:

wakeup( )方法提供了使线程从被阻塞的select( )方法中优雅地退出的能力。

有三种方式可以唤醒在select( )方法中睡眠的线程:

1、调用wakeup()方法,将使得选择器上的第一个还没有返回的选择操作立即返回。

2、调用close()方法,那么任何一个在选择操作中阻塞的线程都将被唤醒,就像wakeup( )方法被调用了一样。与选择器相关的通道

   将被注销,而键将被取消。

 

5、选择过程的可扩展性:

1.一个比较好的策略是对所有的可选择通道使用一个选择器,并将对就绪通道的服务委托给其他线程。我们只需要使用一个线程监控通道的就绪状态并使用一个协调好的工作线程池来处理共接收到的数据。根据部署的条件,线程池的大小是可以调整的(或者它自己进行动态的调整)

2.第二个场景,某些通道要求比其他通道更高的响应速度,可以通过使用两个选择器来解决:一个为命令连接服务,另一个为普通连接服务。与将所有准备好的通道放到同一个线程池的做法不同,通道可以根据功能由不同的工作线程来处理。它们可能可以是日志线程池,命令/控制线程池,状态请求线程池,等等。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值