美文网首页技术杂谈
Java NIO Selector 多路复用选择器

Java NIO Selector 多路复用选择器

作者: 香芋牛奶面包 | 来源:发表于2019-02-16 16:23 被阅读2次

引言

上篇文章我们简单的使用了NIOChannel通道,本期我们主要来介绍一下选择器(Selector
)的使用,SelectorJava NIO核心组件中的一个。在之前介绍5种I/O模型的时候,有介绍过多路复用模型。多路复用模型使得我们可以使用一个线程来管理成千上万的连接,避免了线程上下文切换带来的开销,使得性能得到极大的提升

基本模型

选择器Selector使用的基本模式,跟传统BIO处理模型不一样。传统BIO往往我们会使用多线程来提升处理性能,也就是说每接入一个Client,Server端就会为其新开一个线程,以此来提升并发吞吐量,使用这种模式的弊端很明显,因为线程是系统非常重要的资源,当并发量少的时候,感觉不到,一旦并发量上来,就会出现瓶颈。

再来看Selector是如何处理的,首先每接入一个Client,我们可以通过Selector选择器注册感兴趣的事件,然后通过一个线程去不停的轮询检测各个Client是否有感兴趣的事件发生,有则顺序处理该Client就绪的各个事件。

image.png

Selector 使用实例

我们先来看一个Selector非常常见的使用例子

try {
      // 打开一个通道
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.socket().bind(new InetSocketAddress(8890));
    // 打开 选择器 selector
    Selector selector = Selector.open();
    // 设置非阻塞
    serverSocketChannel.configureBlocking(false);
    // 为ServerSocketChannel注册 OP_ACCEPT 事件,返回一个SelectionKey 对象
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

    while (true) {
            // 返回至少有一个事件就绪的通道数,该方法是阻塞的
        int readyNum = selector.select();
        if (readyNum == 0) {
            continue;
        }
            
            // 返回就绪的 SelectionKey集合
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        Iterator<SelectionKey> iteratorKeys = selectionKeys.iterator();
        
        // 遍历所有就绪的SelectionKey集合
        while (iteratorKeys.hasNext()) {
            SelectionKey key = iteratorKeys.next();
            iteratorKeys.remove();
                 // 判断就绪的具体事件
            if (key.isValid()) {
                if (key.isAcceptable()) {
                    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                    // 接受一个新连接
                    SocketChannel channel = serverChannel.accept();
                    channel.configureBlocking(false);
                    // 为该Channel注册可读事件
                    channel.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    ...
                }
            }
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

通过查看上面的代码,可知我们通过Selector.open()创建了一个选择器,并且通过SocketChannelregister方法注册了选择器和事件,其中register方法会返回一个SelectionKey对象,该对象其实就是维护了ChannelSelector的对应关系

SelectionKey

上文我们提到SelectionKey对象维护了ChannelSelector的对应关系,现在我们来看下
SelectionKey对象内部几个非常重要的属性和方法

  • 属性

    • OP_READ OP_WRITE OP_CONNECT OP_ACCEPT 可注册的四个感兴趣的项目
    • interestOps 所有感兴趣的集合
    • readyOps 所有就绪的兴趣集合
  • 方法

    • boolean isValid() 判断该SelectionKey是否有效
    • void cancel() 取消该SelectionKey中 通道与其选择器 的注册
    • int interestOps() 返回该SelectionKey所包含的所有兴趣(可读可写)集合
    • SelectionKey interestOps(int ops) 设置一个感兴趣的项目
    • int readyOps() 返回已经就绪的兴趣集合
    • boolean isWritable() 判断是否可写
    • boolean isReadable() 判断是否可读
    • boolean isConnectable() 判断是否可连接
    • boolean isAcceptable() 判断是否可接受
    • Object attach(Object ob) 在该SelectionKey中附加一个对象信息
    • Object attachment() 获取附加对象信息

Selector

讲完SelectionKeySelector的关系之后,我们再次回到Selector类,我们首先需要知道Selector中3个重要的SelectionKey集合

  • keys:所有注册到Selector的Channel所表示的SelectionKey都会存在于该集合中。keys元素的添加会在Channel注册到Selector时发生。
  • selectedKeys:该集合中的每个SelectionKey都是其对应的Channel在上一次操作selection期间被检查到至少有一种SelectionKey中所感兴趣的操作已经准备好被处理。该集合是keys的一个子集。
  • cancelledKeys:执行了取消操作的SelectionKey会被放入到该集合中。该集合是keys的一个子集。

接下来将会介绍上文例子中所用到的几个方法

Selector.open()

这个静态方法可以打开一个Selector选择器

public static Selector open() throws IOException {
    return SelectorProvider.provider().openSelector();
}

通过查看源码可知在Linux系统下默认会使用EpollSelectorImpl来作为Selector实现类,注意各个系统下默认的实现类是不同的。

selector.select()

Selectorselect()方法会返回事件就绪的SelectionKey数目(也就是就绪的Channel数)

并且该方法会一直阻塞直到至少一个channel被选择(即,该channel注册的事件发生了)为止,除非当前线程发生中断或者selectorwakeup方法被调用

该方法还有一个重载select(long timeout),可以自定义超时时间

selector.selectNow()

该方法与上面方法类似,但该方法不会发生阻塞,即使没有一个channel被选择也会立即返回

selector.selectedKeys()

返回已就绪的SelectionKey集合,该方法在执行了selector.select()后调用,因为在执行selector.select()后就表示至少有一个SelectionKey已经就绪

尾言

好了,本篇文章就介绍到这里了,篇幅有限加上本人对NIO的理解也有待加深,希望可以在之后更深入的对Java NIO的实现进行分析。

博客原文地址戳这里

相关文章

  • Java NIO Selector

    Java NIO Selector Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能...

  • Java NIO

    java Nio Selector 选择器Buffer 缓冲器Channel 通道 Selector是NIO的核心...

  • Java NIO学习笔记-Selector

    Selector选择器是什么 Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓...

  • 3/27day20_NIO_AIO

    day20 复习 今日内容 NIO的Buffer,Channel,Selector(选择器,多路复用器) AIO是...

  • Java Nio选择器Selector

    Selector Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸...

  • Netty源码系列1--初识Netty

    Java NIO 三件套在 NIO 中有几个核心对象需要掌握:缓冲区(Buffer)、选择器(Selector)、...

  • NIO中选择器Selector

    NIO中选择器Selector 在上一篇的JAVA中NIO再深入我们学会了如何使用Buffer,而在Java中IO...

  • Java NIO

    Java 1.4 开始引入 NIO 框架,提供了 Channel(通道)、Selector(IO复用器/选择器)、...

  • Java NIO Selector 多路复用选择器

    引言 上篇文章我们简单的使用了NIO的Channel通道,本期我们主要来介绍一下选择器(Selector)的使用,...

  • NIO之六-Selector

    Java NIO Selector Why Use a Selector? Creating a Selector...

网友评论

    本文标题:Java NIO Selector 多路复用选择器

    本文链接:https://www.haomeiwen.com/subject/xcqieqtx.html