NIO非阻塞通信简介及基于TCP的非阻塞通信

NIO非阻塞通信简介及基于TCP的非阻塞通信通信方式简述阻塞式的通信以基于TCP的通信方式来说明:创建ServerSocket(或ServerSocketChannel)、调用accept()方法获取客户端的Socket连接、通过Socket通信、关闭Socket连接。对于阻塞式的通信,最影响效率的其实是SocketChannel.read()或通过Socket获得的输入流的InputStream.read()。当服务端调用这些输入方法时,线程就会进入阻塞状态,直到客户端发来数据才被唤醒。如果用单
摘要由CSDN通过智能技术生成

NIO非阻塞通信简介及基于TCP的非阻塞通信

通信方式简述

阻塞式的通信

以基于TCP的通信方式来说明:创建ServerSocket(或ServerSocketChannel)、调用accept()方法获取客户端的Socket连接、通过Socket通信、关闭Socket连接。

对于阻塞式的通信,最影响效率的其实是SocketChannel.read()或通过Socket获得的输入流的InputStream.read()。当服务端调用这些输入方法时,线程就会进入阻塞状态,直到客户端发来数据才被唤醒。

如果用单线程的方式实现上述流程,资源的利用率极低。线程调用read()方法后,进入阻塞状态,如果客户端迟迟没有数据,服务端就会一直被阻塞,而没办法进行其他操作。

第一种解决方法就是使用多线程的方式,一个线程仅负责accept()接收请求,将接收到的请求分配给其他线程,这样,调用read()时,处理请求的线程不会被阻塞,这样就还能接收新的请求,提高了并发量与资源利用率。

第二种解决方法就是使用非阻塞的通信方式。

非阻塞式的通信

先来考虑一下为什么会有阻塞通信?和本地I/O一样,本地I/O也是需要阻塞,当通过系统调用I/O完成后,唤醒被阻塞的进程(线程)。问题就在于:什么时候I/O完成,对于阻塞式的I/O来说,当进程被唤醒时就说明I/O完成。

现在再来想一下本地I/O最低效的方式,循环的访问I/O完成的状态标志,当状态标志指示I/O完成后,说明I/O结束。这就是一个非阻塞的过程,当调度到这个进程(线程),CPU在循环的访问一个标志位来判断I/O是否完成

把这个思想迁移到网络通信中,现在有多个连接,怎样判断哪个连接有数据过来?给这些连接一个标志,用来表示是否有数据,服务端循环的访问每个连接的这个标志,就可以知道哪个连接的数据已经准备好了

问题貌似解决了,但是想一下,如果所有连接都没有数据,那CPU一直再做“无用功”,浪费资源。

为了避免这种资源浪费,可以使用一个选择器,这个选择器的作用就是把有数据的连接都挑出来,轮询的时候保证每个连接都是有数据需要处理的;如果所有连接都没有数据,选择器被阻塞,直到有某个连接的数据准备好。

关于非阻塞式的通信的基本思想,已经阐述完成,带着这些简单的理解,继续了解NIO提供的非阻塞通信。

Java提供的抽象

上面分析非阻塞I/O时,有两个很重要的点:被选择器选择的连接选择器

NIO提供了它们的抽象:java.nio.channels.SelectableChannelSelector

SelectableChannel

下面给出一段官方文档描述的翻译:
SelectableChannel可以通过Selector复用。
SelectableChannel的实例为了可以被Selector使用,必须使用register()方法注册到Selector的实例;该方法返回一个SelectionKey对象,这个对象代表了这个通道注册到一个选择器中。
SelectableChannel是线程安全的。

看完这些解释如果不懂,可以先接着往下看,看到后面就会理解。

Selector

再谈谈向Selector注册,在注册时,有两个参数,一个是Selector,另一个是一个int类型,它表示事件

在上面一直用“读”/“输入”举例子,如果要让Selector判断一个Selectable是否可读,注册时仅需要调用SelectableChannel.register(Selector, SelectionKey.OP_READ)

SelectionKey.OP_READ表示一个读事件,或者说让选择器判断通道是否可读。

非阻塞通信的使用-TCP

SelectableChannel是一个抽象类,在使用时,没办法直接实例化,查看一下它的子类,巧了,有ServerSocketChannelSocketChannel

看到这两个,就想到了它们可以进行阻塞的通信,那怎么进行非阻塞通信?

1. 创建ServerSocketChannel

这一步和阻塞式的通信一致。

severSocketChannel = ServerSocketChannel.open();

2. 设置使用非阻塞式通信

severSocketChannel.configureBlocking(false);

默认情况下,为阻塞通信,也就是参数为true的情况。

3. 绑定端口

这一步和阻塞式的通信一致。

severSocketChannel.bind(new InetSocketAddress(PORT));

4. 创建Selector

selector = Selector.open();

5. 将ServerSocketChannelOP_Accept事件注册到Selector

severSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

当有连接到来(accept队列中有连接请求),事件被触发,选择器选

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值