深入了解java NIO之Selector(选择器)

作者:rickiyang 时间:2022-02-01 16:39:17 

这一节我们将探索选择器(selectors)。选择器提供选择执行已经就绪的任务的能力,这使得多元 I/O 成为可能。就像在第一章中描述的那样,就绪选择和多元执行使得单线程能够有效率地同时管理多个 I/O 通道(channels)。C/C++代码的工具箱中,许多年前就已经有 select()和 poll()这两个POSIX(可移植性操作系统接口)系统调用可供使用了。许过操作系统也提供相似的功能,但对Java 程序员来说,就绪选择功能直到 JDK 1.4 才成为可行的方案。

下面我们来使用选择器:

通过 Selector.open()方法, 我们可以创建一个选择器:


Selector selector = Selector.open();

将 Channel 注册到选择器中:


channel.configureBlocking(false);

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

注意, 如果一个 Channel 要注册到 Selector 中, 那么这个 Channel 必须是非阻塞的, 即channel.configureBlocking(false);因为 Channel 必须要是非阻塞的, 因此 FileChannel 不能够使用选择器, 因为 FileChannel 都是阻塞的.

注意到, 在使用 Channel.register()方法时, 第二个参数指定了我们对 Channel 的什么类型的事件感兴趣, 这些事件有:

  • Connect, 即连接事件(TCP 连接), 对应于SelectionKey.OP_CONNECT

  • Accept, 即确认事件, 对应于SelectionKey.OP_ACCEPT

  • Read, 即读事件, 对应于SelectionKey.OP_READ, 表示 buffer 可读.

  • Write, 即写事件, 对应于SelectionKey.OP_WRITE, 表示 buffer 可写.

一个 Channel发出一个事件也可以称为 对于某个事件, Channel 准备好了. 因此一个 Channel 成功连接到了另一个服务器也可以被称为 connect ready.

我们可以使用或运算|来组合多个事件, 例如:


int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

注意, 一个 Channel 仅仅可以被注册到一个 Selector 一次, 如果将 Channel 注册到 Selector 多次, 那么其实就是相当于更新 SelectionKey 的 interest set. 例如:


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

上面的 channel 注册到同一个 Selector 两次了, 那么第二次的注册其实就是相当于更新这个 Channel 的 interest set 为 SelectionKey.OP_READ | SelectionKey.OP_WRITE.

但是Java NIO的selector允许一个单一线程监听多个channel输入。我们可以注册多个channel到selector上,然后然后用一个线程来挑出一个处于可读或者可写状态的channel。selector机制使得单线程管理多个channel变得容易。

下面我们写一个完整的例子,看一下Selector的用法:


//创建选择器
Selector selector = Selector.open();
channel.configureBlocking(false);
//注册通道
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
 //查看selector中的key是否准备好
 int readyChannels = selector.select();
 //小于0超时,等于0没准备好,大于0已经准备完毕
 if(readyChannels == 0) continue;
 //获取选择器中的key
 Set<SelectionKey> selectedKeys = selector.selectedKeys();
 Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
 while(keyIterator.hasNext()) {
   SelectionKey key = keyIterator.next();
   //遍历已选择键集中的每个键,并检测各个键所对应的通道的就绪事件
   if(key.isAcceptable()) {
     // 连接已经被ServerSocketChannel所接受
   } else if (key.isConnectable()) {
     // 连接已经被远程终止.
   } else if (key.isReadable()) {
     // 通道已经准备好读数据
   } else if (key.isWritable()) {
     // 通道已经准备好写数据
   }
   keyIterator.remove();
 }
}

选择器的使用还有很多的细节,我们应该多查看api文档了解各个方法的用法。下一节我们做一个综合练习,总结一下NIO的使用。

来源:https://www.cnblogs.com/rickiyang/p/11074240.html

标签:java,nio,selector,选择器
0
投稿

猜你喜欢

  • springboot启动脚本start.sh和停止脚本 stop.sh的详细教程

    2022-10-11 08:28:26
  • Spring AOP实现打印HTTP接口出入参日志

    2021-10-09 13:38:37
  • Java中的synchronized关键字

    2023-07-28 18:39:26
  • android编程实现设置、打开wifi热点共享供他人连接的方法

    2021-12-05 03:39:41
  • Kotlin四大组件中的broadcast广播

    2023-07-08 00:44:08
  • 详解Servlet3.0新特性(从注解配置到websocket编程)

    2023-08-08 14:29:48
  • 关于maven打包时的报错: Return code is: 501 , ReasonPhrase:HTTPS Required

    2022-09-09 00:50:51
  • 浅析Java中的异常处理机制

    2021-08-19 05:42:48
  • 你所不知道的Spring的@Autowired实现细节分析

    2023-11-25 05:40:47
  • C#中的multipart/form-data提交文件和参数

    2021-09-26 09:40:19
  • 详解IntelliJ IDEA创建spark项目的两种方式

    2023-12-05 05:48:21
  • 详解C#如何实现分割视频

    2022-12-07 10:48:23
  • spring mvc中@RequestBody注解的作用说明

    2022-04-07 14:15:02
  • Android账号注册实现点击获取验证码倒计时效果

    2023-05-18 05:46:33
  • 详解SpringBoot中的tomcat优化和修改

    2022-12-31 04:00:44
  • C#中值类型和引用类型的区别深度分析

    2022-07-21 21:29:26
  • XAML如何获取元素的位置

    2023-03-16 14:24:12
  • C#基于正则表达式删除字符串中数字或非数字的方法

    2021-07-12 17:27:44
  • C#自定义日志记录

    2021-06-11 23:44:47
  • Maven Repository仓库的具体使用

    2021-11-04 21:33:34
  • asp之家 软件编程 m.aspxhome.com