NIO与Socket笔记 :Selector 的使用

选择器与 I/O 多路复用

Selector选择器是 NIO 技术中的核心组件,可以将通道注册进选择器中,

其主要作用就 是使用 1个线程来对多个通道中的已就绪通道进行选择,然后就可以对选择的通道进行数据 处理,属于一对多的关系,

也就是使用1个线程来操作多个通道,这种机制在 NIO 技术中 称为“ I/O 多路复用” 。

它的优势是可以节省 CPU 资源,因为只有 1 个线程, CPU 不需要在 不同的线程间进行上下文切换 。

线程的上下文切换是一个非常耗时的动作,减少切换对设计 高性能服务器具有很重要的意义 。

从在图 5-1 中可以发现,如果不使用 1/0 多路复用, 则 需要创建多个线程对象,每个线 程都对应一个通道,在对应的通道中进行数据的处理 。 但是,如果在高并发环境下,就会 创建很多的线程对象,造成内存占用率升高,增加 CPU 在多个线程之间上下文切换的时间, 因此,此种设计就不适用于高并发的场景 。

从图 5-2 中可以 发现,使用了 I/O 多路复用后,只需要使用 1 个线程就可 以操作多个通 道,属于一对多的关系 。 它和“不使用 I/O 多路复用”相比最大的优势就是内存占用率下降 了,因为线程对象的数量大幅减少,还有 CPU 不需要过多的上下文切换,这对高并发高频 段处理的业务环境有非常重要的优势 。

线程数会随着通道的多少而动态地增减以进行适配,在内部其实并不永远是一个线程,多 路复用的核心目的就是使用最少的线程去操作更多的通道 。

扫描二维码关注公众号,回复: 5820677 查看本文章

在 JDK 的源代码中,创建线程的 个数是根据通道的数量来决定的,每注册 1023 个通道就创建 l 个新的线程,这些线程执行 Windows 中的 selectQ 方法来监测系统 socket 的事件,如果发生事件则通知应用层 中的 main 线程 终止阻塞,继续向下运行,处理事件。 可以在CMD中使用jps和jstack来查看创建线程的数量。

学习 I/O 多路复用时一定要明白一个知识点,就是在使用 I/O 多路复用时,这个线 程不是以 for循环的方式来判断每个通道是否有数据要进行处理,而是以操作系统底 层作为“通知器”,来“通知 JVM 中的线程”哪个通道中的数据需要进行处理,这点一定要注意 。 当不使用 for 循环的方式来进行判断,而是使用通知的方式时,这就 大大提高了程序运行的效率,不会出现无限期的 for 循环迭代空运行了

核心类 Selector、SelectionKey和 SelectableChannel 的关系

在使用选择器技术时,主要由 3 个对象以合作的方式来实现线程选择某个通道进行业务处理,

这 3 个对象 分别是 Selector、 SelectionKey 和 SelectableChannel。

Selector类是抽象类,它是 SelectableChannel对象的 多路复用器。

只有 SelectableChannel通道对象才能被 Selector.java选择器所复用.

因为只有 SelectableChannel类才具有 register(Selector sel, int ops)方法,该方法的作用是将当前的 SelectableChannel通道注册到指定的选择器中,参 数 sel 也说明了这个问题 。

由选择器来决定对哪个通道中的数据进行处理,这些能被选择器所处理的通道的父类 就是 SelectableChannel,它是抽象类,

SelectableChannel类和 FileChannel类是平级关系,都继承自父类 AbstractlntenuptibleChannel。

SelectableChannel类的子类都可以使用 register (Selector sel, int ops)方法将自身注册到选择器中 。

SelectionKey 类的作用 是一 个标识,这个标 识代表 SelectableChannel类已经向 Selector类注 册了 。

通道类 AbstractlnterruptibleChannel与接口 InterruptibleChannel的介绍

AbstractlnterruptibleChannel类实现了 lnterruptibleChannel接口,该接口的主要作用, 是使通道能 以异步的方式进行关闭与中断 。

如果通道实现了 asynchronously 和 closeable 特性,那么,当一个线程在一个能被中断 的通道上出现了阻塞状态,

其他线程调用这个通道的 close()方法时,这个呈阻塞状态的线 程将接收到 AsynchronousC!oseException异常。

如果通道实现了 asynchronously 和 closeable,并且还 实 现了 interruptible 特性,

那么,当一个线程在一个能被中断的通道上出现了阻塞状态,其他线程调用这个阻塞线程的 interrupt()方法时,

通道将被关闭,这个阻塞的线程将接收到 ClosedByInterruptException异 常,这个阻塞线程的状态一直是中断状态 。

通道类 SelectableChannel 的介绍

AbstractlnterruptibleChannel类的子类就包含抽象类 SelectableChannel和 FileChannel。

一个通道至多只能在任意特定选择器上注册一次。

可以通过调用 isRegistered()方法来确定是否已经向一个或多个选择器注册了某个通道 。

SelectableChannel 在多线程并发环境下是安全的 。

SelectableChannel要么处于 阻塞模式,要么处于非阻塞模式 。

在阻塞模式中,每一个I/O 操作完成之前都会阻塞在其通道上调用的其他I/O  操作 。

在非阻塞模式中,永远不会阻 塞 I/O操作,并且传输的字节可能少于请求的数量,或者可能根本不传输字节。

可通过调用 SelectableChannel 的 isBlocking()方法来确定其是否为阻塞模式 。

新创建 的 SelectableChannel 总是处于阻塞模式 。

在结合使用基于选择器的 多路复用时 , 非阻塞模式是最有用的 。

向选择器注册某个通道前 , 必须将该通道置于非阻塞模式, 并且在 注销之前可能无法返回到阻塞模式

 

通道类 AbstractSelectableChannel 的介绍

抽象类 AbstractSelectableChannel是可选择通道的基本实现类。

此类定义了处理通道注册、 注销 和关闭机制的各种方法。

它会维持此通道的 当前阻 塞模式及其当前的选择键集 SelectionKey。

它执行 实现 SelectableChannel 规范所需 的所有 同步 。

此类 中所定义的抽象保护方法的实现不必与同一操作中 使用的其他线程同步。

通道类 Server如 cketChannel 与接口 NetworkChannel 的介绍

ServerSocketChannel 类是针对面 向流的侦昕套接字 的可选择通道 。

不是侦听网络套接字的完整抽象,必须通过调用 socket()方法所获得的关联 ServerSocket对 象来完成对套接宇选项的绑定和操作 。

不可能为任意的已有 ServerSocket 创建通道,也不可 能指定与 ServerSocketChannel关联的 ServerSocket所使用的 Socketlmpl对象。

通过调用此类的 open()方法创建 ServerSocketChannel。

新创建的 ServerSocketChannel 己打开,但尚 未绑定 。

多个并发线程可安全地使用服务器套接字通道 ServerSocketChannel。

ServerSocketChannel 类、 Selector 和 SelectionKey 的使用

获得 ServerSocketChannel 与 ServerSocket socket 对象

执行绑定操作

serverSocket .bind( new InetSocketAddress( "localhost ” , 8888));

执行绑定操作与设置 backlog

public abstract ServerSocketChannel bind(SocketAddress local, int backlog)方法的作用

是将通道的套接字绑定到本地地址并侦昕连接,通过使用参数 backlog来限制客户端连接的 数 量。

服务端允许接受的客户端连接个数上限为 50。

阻塞与非阻塞以及 accept()方法的使用效果

public abstract SocketChannel accept()方法的作用是接受此通道套接字的连接。

如果此 通道处于非阻塞模式,那么在不存在挂起的连接时,此方法将直接返回 null。 否则,在新的 连接可用或者发生I/O错误之前会无限期地阻塞它。 无论此通道的阻塞模式如何,此方法返回的套接字通道(如果有)将处于阻塞模式 。

如何切换 ServerSocketChannel通道的阻塞与非阻塞的执行模式呢?

调用 ServerSocket­ Channel 的 public final SelectableChannel configureBlocking(boolean block)方法即可 。

public final SelectableChannel configureBlocking(boolean block)方法的作用是调整此通道的阻塞模 式,

传入 true是阻塞模式 ,传入 false是非阻塞模式。

获得 Selector 对象

执行注册操作与获得 SelectionKey 对象

SelectableChannel类的 publicfinal SelectionKey register(Selector sel, int ops)方法的作用 是向给定的选择器注册此通道,

返回一个选择键 (SelectionKey)。

参数 sel代表要向其注册此通道的选择器,参数 ops代表 register()方法的返回值 Selection­ Key 的可用操作 集 ,操作 集 是在 SelectionKey 类中以常 量 的形式进行提供的,如图 5-26 所示。

方法 public final SelectionK.ey register(Selectorsel, int ops)的 ops参数就是通道感兴趣的事件,也就是通道能执行操作的集合,可以对 ops 执行位运 算 。

判断注册的状态

SelectableChannel类的 publicfinal boolean isRegistered()方法的作用是判断此通道当前是否 已向任何选择器进行了注册 。

新创建的通道总是未注册的 。

由于对 SelectionKey 执行取消操作 和通道进行注销之间有延迟,因此在已取消某个通道的所有 SelectionKey后,该通道可能在一 定时间内还会保持已注册状态。

关闭通道后,该通道可能在一定时间内还会保持已注册状态。

将通道设置成非阻塞模式再注册到选择器

在将通道注册到选择器之前,必须将通道设置成非阻塞模式 

使用 configureBlocking (false)方法解决异常

public final SelectableChannel configureBlocking(boolean block)方法的作用是调整此通 道的阻塞模式。如果向一个或多个选择器注册了此通道,则尝试将此通道置于阻塞模式将导 致抛出 IllegalBlockingModeException。 可在任意时间调用此方法 。新 的阻塞模式仅影响在 此方法返回后发起 的 1/0 操作 。 对于某些实现,这可能需要阻塞,直到所有挂起的 1/0 操作 已完成 。 如果调用此方法的同时正在进行另一个此方法或 register()方法的调用,则在另一 个操作完成前将首先阻塞该调用 。

public final boolean isBlocking()方法的作用是判断此通道上的每个 1/0操作在完成前是 否被阻塞 。 新创建的通道总是处于阻塞模式。如果此通道已关闭, 则此方法返回的值是未指 定的。

判断打开的状态

public final boolean isOpen()方法的作用是判断此通道是否处于打开状态。

获得阻塞锁对象

public final Object blockingLock()方法的作用是获取其 configureBlocki吨()和 register() 方法实现同步的对象,防止重复注册 。

获得支持的 SocketOption 列表

Set< SocketOption<?>> supportedOptions()方法的作用是返回通道支持的 Socket Option。

获得与设置 SocketOption

public abstract <T> ServerSocketChannel setOption(SocketOption<T> name, T value)方法 的作用是设置 SocketOption值。

< T> T getOption(SocketOption<T> name)方法的作用是获取 Socket Option值 。

获得 SocketAddress 对象

public abstract SocketA ddress getLocalAddress()方法的作用是获取绑定的 SocketAddress 对象。

阻塞模式的判断

public final boolean isBlocking()方法的作用是判断此通道上的每个 1/0操作在完成前是 否被阻塞 。

新创建的通道总是处于阻塞模式 。

如果此通道已 关 闭,则此方法返回的值是未指 定的 。 返回值代表当且仅当此通道处于阻塞模式时才返回 true。

根据 Selector找到对应的 SelectionKey

public final SelectionKey keyFor(Selector sel)方法的作用是获取通道向给定选择器注册 的 SelectionKey。

同一个 SelectableChannel通道可以注册到不同的选择器对象,然后返回新创建的 Selection­ Key对象, 可以使用 public final SelectionKey keyFor(Selector sel)方法来取得当前通道注册 在指定选择器上的 SelectionKey对象。

 

获得 SelectorProvider 对象

public final SelectorProvider provider() 方法 的作 用是返回创建此通道的 SelectorProvider。

SelectorProvider类的作用是用于选择器和可选择通道的服务提供者类。

通道注册与选择器

1. 相同的通道可以注册到不同的选择器,返回的 SelectionKey 不是同一个对象

2. 不同的通道注册到相同的选择器,返回的 SelectionKey 不是同一个对象

3. 不同的通道注册到不同的选择器,返回的 Selectionkey 不是同一个对象

4. 相同的通道重复注册相同的选择器,返回的 SelectionKey 是同一个对象

返回此通道所支持的操作

public final intvalidOps()方法的作用是返回一个操作集,标识此通道所支持的操作。

执行 Connect 连接操作

public abstract boolean connect(SocketAddress remote)方法的作用是连接到远程通道的 Socket。

如果此通道处于非阻塞模式, 则此方法的调用将启动非阻塞连接操作 。

如果通道呈阻塞模式, 则立即发起连接;

如果呈非阻塞模式, 则不是立即发起连接,而 是在随后的某个时间才发起连接 。

如果连接是立即建立的,说明通道是阻塞模式,当连接成功时, 则此方法返回 true,连 接失败出现异常。

如果此通道处于阻塞模式, 则此方法的调用将会阻塞,直到建立连接或发生 I/O错误。

如果连接不是立即建立的,说明通道是非阻塞模式, 则此方法返回false,并且以后必

须通过调用fishConnect()方法来验证连接是否完成 。

判断此通道上是否正在进行连接操作

public abstract boolean isConnectionPending()方法的作用是判断此通道上是否正在进行连 接操作。

返回值是 true代表当且仅当已在此通道上发起连接操作,但是尚未通过调用 finish­ Connect()方法完成连接。

还可以是在通道 accept()之后和通道 close()之前, isConnection­ Pending()方法的返回值都是 true。

完成套接字通道的连接过程

public abstract boolean finishConnect()方法的作用是完成套接字通道的连接过程 。

通过 将套接字通道置于非阻塞模式,然后调用其 connect()方法来发起非阻塞连接操作 。

如果连 接操作失败,则调用此方法将导致抛出 IOException。

一旦建立了连接,或者尝试已失败,该套接字通道就变为可连接的,并且可调用此方法完成连接序列 。

如果已连接了此通道,则不阻 塞 此方法并且立即返回 true。

如果此通道处于非阻塞模式,那么当连接过程尚未完成时,此方法将返回 false。

如果此通道处于阻塞模式,当连接成功之后返回 true,连接失败时抛出描述该失败的 、经过检查的异常。

在连接完成或失败之前都将阻塞此方法

虽然可在任意时间调用此方法 但如果正在调用此 方法 时在此通道上调用 读取或写入操作,则在此调用完成前将首先阻塞该操作 。

类 FileChannel 中的 long transferTo (position, count, WritableByteChannel)方法

方法 transferTo()的作用是试图读取此通道文件中给定 position处开始的 count个 字节,并将其写入目标通道中,但是此方法的调用不一定传输所有请求的字节 ,是否传 输取决于通道的性质和状态。

方法 publicstatic SocketChannel open (SocketAdd陪ss remote)与 Socket­Option 的执行顺序

如果先调用 publicstatic SocketChannel open(SocketAddress remote)方法,然后设置 Sock时,Option, 则不会出现预期的效果,因为在publicstatic SocketChannel open(SocketAddressremote)方法中已 经自 动执行了 connect()方法

传输大文件

 

验证read 和 write 万法是非阻塞的

执行代码 configureBlocking(false)代表当前的 1/0为非阻塞的,

NIO就是同步非阻塞模 型,所以 read和 write方法也呈现此特性

猜你喜欢

转载自blog.csdn.net/zhanglong_4444/article/details/89025607
今日推荐