Java NIO 中的 Selector 详解(上)

简介: Java NIO 中的 Selector 详解

Selector 简介


1、Selector 和 Channel 关系


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


image.png


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


2、可选择通道(SelectableChannel)


(1)不是所有的 Channel 都是可以被 Selector 复用的。比方说, FileChannel 就不能被选择器复用。判断一个 Channel 能被 Selector 复用,有一个前提:判断他是否继承了一个抽象类 SelectableChannel。如果继承了 SelectableChannel , 则可以被复用,否则不能。


(2)SelectableChannel 提供了实现通道选择性所需要的公共方法。它是所有支持就绪检查通道类的父类,所有 socket 通道,都继承 SelectableChannel 类都是可选择的,包括从管道(Pipe) 对象的中获取得到的通道。而 FileChannel 类,没有继承 SelectableChannel , 因此是不是可选通道。


(3)一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。通道和选择器之间的关系,使用注册的方式完成。SelectableChannel 可以被注册到 Selector 对象上,在注册时候,需要指定通道的那些操作,是 Selector 感兴趣的。


image.png


3、Channel 注册到 Selector


(1)使用 Channel.register(Selector sel, int pos) 方法,将一个通道注册到一个选择器时。第一个参数,指定通道要注册的选择器。第二个参数指定选择器需要查询的通道操作。


(2)可供选择器查询的通道操作,从类型类分,包括一下四种:


  • 可读:SelectionKey.OP_READ
  • 可写:SelectionKey.OP_WRITE
  • 连接:SelectionKey.OP_CONNECT
  • 接收:SelectionKey.OP_ACCEPT


如果 Selector 对通道的多操作类型感兴趣,可以用“位或”操作符来实现:

比如 int key = SelectionKey.OP_READ | SelectionKey.OP_WRITE;


(3)选择器查询的不是通道的操作,而是通道的某个操作的一种就绪状态。什么操作的就绪状态?一旦通道具备完成某个操作的条件,表示该通道的某个操作已经就绪,就可以被 Selector 查询到,程序可以对通道进行对应的操作。比方说,某个 SocketChannel 通道可以连接到一个服务器,则处于“连接就绪”状态(OP_CONNECT)。 再比方说,一个ServerSocketChannel 服务器通道准备好接收新进入的连接,则处于“接收就绪”(OP_ACCEPT)状态。还比方说,一个数据可读的通道,可以说是“读就绪”(OP_READ)。一个等待写数据的通道可以说是“写就绪”(OP_WRITE)。


4、选择键(SelectionKey)


(1)Channel 注册之后,并且一旦通道处于某种就绪状态,就可以被选择器查询到。这个工作使用选择器 Selector 的 select() 方法完成。select 方法的作用,对感兴趣的通道操作,进行就绪状态的查询。


(2)Selector 可以不断的查询 Channel 中发生的操作的就绪状态。并且选择甘心去的操作就绪状态。一旦通道有操作的就绪状态达成,并且是 Selecor 感兴趣的操作,就会被 Selector 选中,放入选择键集合中。


(3)一个选择键,首先包含了注册在 Selector 的通道操作的类型,比方说: SelectionKey.OP_READ . 也包含了特定的通道与特定的选择器之间的注册关系。

开发应用程序是,选择键是编程的关键,NIO 编程,就是更具对应的选择键,进行不同的业务逻辑处理。


(4)选择键的概念,和事件的概念比较相似。一个选择键类似监听器模式里面的一个事件。由于 Selector 不是事件触发的模式,而是主动去查询的模式,所以不叫事件 Event, 而是叫 SelectionKey 选择键。


Selector 的使用方法


1、Selector 的创建


通过 Selector.open() 方法创建一个 Selector 对象。如下;


// 获取 Selector 选择器
Selector selector = Selector.open();


2、注册 Channel 到 Selector


要实现 Selector 管理 Channel , 需要将 channel 注册到相应的 Selector 上


// 1. 获取 Selector 选择器
Selector selector = Selector.open();
// 2. 获取通道
ServerSocketChannel socketChannel = ServerSocketChannel.open();
// 3. 设置为非阻塞
socketChannel.configureBlocking(false);
// 4. 绑定连接
socketChannel.bind(new InetSocketAddress(9999));
// 5. 将通道注册到选择器
socketChannel.register(selector, SelectionKey.OP_ACCEPT);


上面通过调用通道的 register() 方法会将它注册到一个选择器上。


需要注意的是:


(1)与 Selector 一起使用, channel 必须处于非阻塞模式下,否则将抛出异常 IllegalBlockingModeException 。 这意味着,FileChannel 不能与 Selector 一起使用,因为 FileChannel 不能切换到非阻塞模式,而套接字相关的所有通道都可以。


(2)一个通道,并没有一定要持有所有的四种操作。比如服务器通道 ServerSocketChannel 支持 Accept 接收操作,而 SocketChannel 客户端通道则不支持。可以通过通道上的 vildOps() 方法,来获取特定通道下所支持的操作集合。


3、轮训查询就绪操作


(1) 通过 Selector 的 select() 方法, 可以查询出已经就绪的通道操作,有些就绪的状态集合,包含在一个元素是 Selectionkey 对象的 Set 集合中


(2) 下面是 Selector 几个重载的查询 select() 方法:


  • select() 阻塞到至少有一个通道在你注册的事件上就绪。
  • select(long timeout) 和 select() 一样,但最长阻塞事件为 timeout 毫秒。
  • selectNow() 非阻塞,只要有通道就立即返回。


select() 方法返回的 int 之,表示有多少通道已经就绪,准确的说目前一次 select

方法来到这一次 select 方法之间的时间段上,有多少个通道编程了就绪状态。


例如:首次调用 select() 方法,如果有一个通道编程了就绪状态,返回了 1 , 若子啊次调用 select() 方法,如果另外一个通道就绪了,它会再次返回 1。 如果第一个就绪的 chnanel 么有做任何操作,现在就有两个就绪通道,但是每次 select() 方法调用之间,只有一个通道就绪了。


一旦调用 select() 方法,并且返回值部位 0 时,在 Selector 中有一个 seletedKeys() 方法,用来范围已选择键集合,迭代集合的每个以元素,根据就绪操作的类型,完成对应的操作


// 查询已经就绪的通道操作
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
    SelectionKey key = iterator.next();
    // 判断 key 就绪状态操作
    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
    }
}
iterator.remove();


相关文章
|
4月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
15天前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
1月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
30 2
|
2月前
|
存储 网络协议 Java
Java NIO 开发
本文介绍了Java NIO(New IO)及其主要组件,包括Channel、Buffer和Selector,并对比了NIO与传统IO的优势。文章详细讲解了FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel及Pipe.SinkChannel和Pipe.SourceChannel等Channel实现类,并提供了示例代码。通过这些示例,读者可以了解如何使用不同类型的通道进行数据读写操作。
Java NIO 开发
|
3月前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
84 2
|
3月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
116 0
|
4月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
165 1
|
3月前
|
存储 网络协议 Java
【Netty 神奇之旅】Java NIO 基础全解析:从零开始玩转高效网络编程!
【8月更文挑战第24天】本文介绍了Java NIO,一种非阻塞I/O模型,极大提升了Java应用程序在网络通信中的性能。核心组件包括Buffer、Channel、Selector和SocketChannel。通过示例代码展示了如何使用Java NIO进行服务器与客户端通信。此外,还介绍了基于Java NIO的高性能网络框架Netty,以及如何用Netty构建TCP服务器和客户端。熟悉这些技术和概念对于开发高并发网络应用至关重要。
72 0
|
4月前
|
安全 Java
【Java】已解决java.nio.channels.OverlappingFileLockException异常
【Java】已解决java.nio.channels.OverlappingFileLockException异常
124 1
|
4月前
|
Java
【Java】已解决java.nio.channels.ClosedChannelException异常
【Java】已解决java.nio.channels.ClosedChannelException异常
403 1
下一篇
无影云桌面