客户端,运行结果如下:
文章插图
本例中测试的客户端数量是 30,服务端使用 java 线程池来处理任务,线程数量为 5 个,服务端不用为每个客户端都创建一个线程,由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机 。
在活动连接数不是特别高的情况下,这种模型是还不错,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题 。
但是,它的底层仍然是同步阻塞的 BIO 模型,当面对十万甚至百万级连接的时候,传统的 BIO 模型真的是无能为力的,我们需要一种更高效的 I/O 处理模型来应对更高的并发量 。
6.4.3、NIONIO 中的 N 可以理解为 Non-blocking,一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入,对应的在java.nio包下 。
NIO 新增了 Channel、Selector、Buffer 等抽象概念,支持面向缓冲、基于通道的 I/O 操作方法 。
NIO 提供了与传统 BIO 模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现 。
NIO 这两种通道都支持阻塞和非阻塞两种模式 。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反 。
对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发效率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发 。
我们先看一下 NIO 涉及到的核心关联类图,如下:
文章插图
上图中有三个关键类:Channel 、Selector 和 Buffer,它们是 NIO 中的核心概念 。
- Channel:可以理解为通道;
- Selector:可以理解为选择器;
- Buffer:可以理解为数据缓冲流;
还有一个 Buffer 类,你可以将它看作为 IO 中 Stream,但是它比 IO 中的 Stream 更加具体化,我们可以将它比作为车上的座位,Channel 如果是汽车的话,那么 Buffer 就是汽车上的座位,Channel 如果是高铁上,那么 Buffer 就是高铁上的座位,它始终是一个具体的概念,这一点与 Stream 不同 。
Socket 中的 Stream 只能代表是一个座位,至于是什么座位由你自己去想象,也就是说你在上车之前并不知道这个车上是否还有没有座位,也不知道上的是什么车,因为你并不能选择,这些信息都已经被封装在了运输工具(Socket)里面了 。
NIO 引入了 Channel、Buffer 和 Selector 就是想把 IO 传输过程中涉及到的信息具体化,让程序员有机会去控制它们 。
当我们进行传统的网络 IO 操作时,比如调用 write() 往 Socket 中的 SendQ 队列写数据时,当一次写的数据超过 SendQ 长度时,操作系统会按照 SendQ 的长度进行分割的,这个过程中需要将用户空间数据和内核地址空间进行切换,而这个切换不是程序员可以控制的,由底层操作系统来帮我们处理 。
而在 Buffer 中,我们可以控制 Buffer 的 capacity(容量),并且是否扩容以及如何扩容都可以控制 。
理解了这些概念后我们看一下,实际上它们是如何工作的呢?
还是以上面的操作为例子,为了方便观看结果,本次的客户端线程请求数改成 15 个 。
客户端,程序如下:
文章插图
服务端,程序如下:
文章插图
先启动服务端程序,再启动客户端程序,看看运行结果!
服务端,运行结果如下:
文章插图
客户端,运行结果如下:
文章插图
推荐阅读
- 马航MH370事件再现谜团 马航mh370失踪之谜:失联真相终于浮出水面
- 裁员|12家互联网大厂被约谈没多久,腾讯再度裁员,职场你还受得住么?
- 世界上真的有人会通灵吗 为什么真的有通灵人
- 特异功能是真的吗 有人有特异功能吗
- 别再被4s店修理厂忽悠了!这3个常见发动机保养,并不用经常做
- 专家提醒,现炒茶至少需存放十天再喝
- 汽车副厂件和原厂件有何区别别再傻傻分不清
- 女人到了中年别再装嫩,学学她们的“大气穿搭”,优雅大方显气质
- 梦见有人想非礼我但没成功但是,有男人救了我 梦见有人想非礼我但没成功但我裸体跑了
- 孕妇梦见自己在空中飞来飞去 梦见自己在空中飞来飞去的有人在追