NIO 编程一直是 Java 知识体系中的一个重点. 前几年的时间面试的门槛是了解 NIO, 现在就不一样了, 最起码也要精通 NIO, 因此学习 javaNIO 编程是非常有必要的. 这篇文章就开始对 NIO 进行一个认识. 本文参考了慕课网, 特在此说明.
一, 认识 NIO
1, 什么是 BIO?
想要学习 NIO, 那我们就必须先要认识一下 BIO, 在 JDK1,4 之前, 我们使用网络连接的时候一直都是使用的 BIO, 也就是阻塞式, 网络模型是下面这个样子的.
上面这个网络模型是这样的.
(1)server 创建初始化一些预备工作之后, 就开始等待客户端 client 的链接
(2)client 开始链接 server.
(3)server 一旦请求到 client 的请求之后就会开启一个线程去处理.
就好比是只有一家餐饮店, 每进来一个顾客, 我们就需要去创建一个线程去处理. 这就是 BIO. 他的缺点可想而知. 如果客户端很多的话, server 就必须要开启很多个 Thread 去处理, 这样也太麻烦了. 毕竟像淘宝微信这样的平台好几亿人再用, 而且请求量这么大, 总不能开启几亿个线程去处理吧. 这时候在 jdk1.4 就出现了 NIO.
2, 出现了 NIO
既然 BIO 有这么多的缺点, java 官方肯定也明白, 于是在 jdk1.4 的时候及时的加入了 NIO.
这个跟上一个的区别我们来捋一下:
(1) 一个客户端进来之后首先加入到 Set 中
(2)server 时刻轮询着这个 set, 一旦发现有客户端连接进来就开始 handler
(3) 多个 client 连接进来的时候, 都保存在这个 set 中, 这样我们就可以轮询处理多个 client 了.
这就 NIO, 他的优点从上面的图也可以看出来. 我们可能只需要创建一个 Thread 就可以处理所有的 client 了. 当然每一个 client 要做的事情不一样, 有的是连接请求, 有的是读写请求, 这时候 server 就可以根据不同的请求使用不同的 handler 了. 再给出一张图看一下:
当然, 这只是列举出了 NIO 的特点, 还有大致网络模型, 想要去真正的了解他, 还是代码来的直接.
二, 代码实现
1, 基本概念
在正式开始代码的编写之前, 我们还要先认识一下涉及到的几个类.
(1)channel
它相当于是一个通道, 这个通道是流通数据的, 我们既可以从通道中读取数据, 又可以写数据到通道. 常见的 channel 有四个: FileChannel,DatagramChannel,SocketChannel,ServerSocketChannel.
FileChannel 从文件中读写数据.
DatagramChannel 能通过 UDP 读写网络中的数据.
SocketChannel 能通过 TCP 读写网络中的数据.
ServerSocketChannel 可以监听新进来的 TCP 连接, 像 web 服务器那样. 对每一个新进来的连接都会创建一个 SocketChannel.
(2)Buffer
Buffer 用于和通道进行交互. 数据是从通道读入缓冲区, 从缓冲区写入到通道中的.
使用 Buffer 读写数据一般遵循以下四个步骤:
写入数据到 Buffer
调用 flip() 方法
从 Buffer 中读取数据
调用 clear() 方法或者 compact() 方法
(3)Selector
Selector(选择器) 能够检测一到多个 NIO 通道, 并能够知晓通道是否为诸如读写事件做好准备的组件. 这样, 一个单独的线程可以管理多个 channel, 从而管理多个网络连接.
2, 实现步骤
我们在这里实现一个类似于聊天室的案例, 上面已经把 NIO 涉及到的一些核心类说了一下, 下面说一下实现的步骤. 这个步骤是要结合上面的图来理解会比较容易一些:
第一步: 创建 Selector
第二步: 创建 ServerSocketChannel, 绑定监听端口
第三步: 将 Channel 设置为非阻塞模式
第四步: 将 Channel 注册到 Selector 上, 监听连接事件
第五步: 循环调用 Selector 的 select 方法, 检测就绪情况
第六步: 调用 selectedKeys 方法获取就绪 channel 集合
第七步: 判断就绪事件种类, 调用业务处理方法
第八步: 根据业务需要决定是否再次注册监听事件, 重复执行第三步操作
有了这个步骤我们再去代码实现.
3, 代码实现
(1)server 端代码开发
首先我们看一下服务器端
上面把 server 中基本的是步骤实现了. 现在开始真正的去处理一下.
第一种情况: 链接事件处理
第二种情况: 读写时间处理
到了第五步 broadCast 方法其实我们可以对此进行一个变化, 在这里我们实现的是广播到其他所有 client. 但是如果是一对一聊天的话我们就可以单播到指定 client.
这就是整个服务器端的开发, 当然还要客户端的开发, 我们同样来看看.
(2)client 端代码开发
客户端代码说实话就比较轻松一点了.
我们就再来看看, 客户端如何处理服务器端返回的数据.
readHandler 方法是如何读取呢?
到这一步, 整个客户端的代码就算是完成了, 如果你仔细的捋一遍, 其实整个流程还是很清晰的.
三, 总结
虽然 NIO 这么好其实还是有很多缺点的, 在上面的代码量其实你就可以发现了, 大量的代码使得我们在构建复杂系统的时候超级麻烦, 有时候正是这些技术的不完备, 才造成了我们程序员工作量大, 压力大, 但是科技的进步毕竟是要一点一点发展的嘛. 另外说一句这个 NIO 还有一个大坑, 就是 Selector 空轮询的时候, 导师 CPU100%. 不过这种情况我还没试过.
想要精通 NIO 的话, 这篇文章真的远远不够, 顶多算是入门把. 想要真正认识我觉得首先要深入源码, 然后就是实际场景中的使用, 不过目前来看的话 netty 和 mina 框架要比 java 的 NIO 好的多, 不单单是性能, 更重要的是我们的开发效率. 算是在一定程度上避免了我们程序员 "钱多话少死得快" 的现象了吧.
来源: http://developer.51cto.com/art/201909/602531.htm