为了将Channel和Selector配合使用,channel必须注册到selector上。通过SelectableChannel.register()方法来实现.Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以
基于选择器,Java 的 NIO,用非阻塞的 IO 方式。可以用一个线程,处理多个的客户端连接,避免了多线程之间的上下文切换导致的开销
SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
第二个参数,Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:
-
SelectionKey.OP_CONNECT 某个channel成功连接到另一个服务器称为“连接就绪”
-
SelectionKey.OP_ACCEPT 一个server socket channel准备好接收新进入的连接称为“接收就绪”
-
SelectionKey.OP_READ 一个有数据可读的通道可以说是“读就绪”
-
SelectionKey.OP_WRITE 等待写数据的通道可以说是“写就绪
注意:
- client的socketChannel:只监听 连接、读、写事件
- server的serverSocketChannel 只监听OP_ACCEPT
- server的socketChannel只监听 读、写事件 注册好之后会有一个多路复用器selector去循环检测对于管道上是否有该事件发生。
注册后返回一个 SelectionKey,SelectionKey,表示 Selector 和网络通道的注册关系
通过SelectionKey可以获取与之关联的Selector,channel,再完成后续业务处理
Selector 常用方法
public abstract class Selector implements Closeable {
public static Selector open();//得到一个选择器对象
public int select();//阻塞到至少有一个通道在你注册的事件上就绪了。返回有事件发生的通道的个数.
public int select(long timeout);//监控所有注册的通道,当其中有 IO 操作可以进行时,将
对应的 SelectionKey 加入到内部集合中并返回,参数用来设置超时时间,返回有事件发生的通道的个数.
public int selectNow();//不阻塞,立马返还。此方法执行非阻塞的选择操作。如果自从前一次选择操作后,没有通道变成可选择的,则此方法直接返回零,返回有事件发生的通道的个数.
public Set<SelectionKey> selectedKeys();//从内部集合中得到所有的 SelectionKey
public abstract Selector wakeup();//某个线程调用select()方法后阻塞了,即使没有通道已经就绪,也有办法让其从select()方法返回。只要让其它线程在第一个线程调用select()方法的那个对象上调用Selector.wakeup()方法即可。阻塞在select()方法上的线程会立马返回。
public abstract void close() throws IOException;//用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。
}