首页 / JAVA / Java NIO模型
Java NIO模型
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java NIO模型,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3565字,纯文字阅读大概需要6分钟。
内容图文
![Java NIO模型](/upload/InfoBanner/zyjiaocheng/609/415660e3603a4ccc983598cc4ba05ff7.jpg)
NIO
BIO
问题
通过前面两个章节的介绍,相信大家已经对网络BIO模型已经有了不少了解了。那么BIO主要的问题点在哪里呢?我们来回顾下BIO模型的架构图:
client与service经历三次握手之后,service在内核中开辟空间,分配FD,socket主线程bind到该FD后,clone出子线程来接收client传输的数据以及业务处理。
如果简单的使用此模型,我们可以看到每一次接收到client连接,都需要创建出新线程,而我们也知道计算机创建新线程的代价是很大的,内存分配(每个线程约需几MB),以及线程调度等,这一块会极大的占用系统资源,并且子线程的IO读取,如果线程量很大,会极大降低IO读取的效率。
既然在创建线程这块是个极大的影响点,我们将clone线程的操作换成线程池会如何呢?
使用线程池,虽然解决了clone线程带来的同步消耗资源的问题,但是对IO读取,以及线程切换,资源实际消耗等问题,并没有带来实质性的改善。
究其根本原因,是因为kernel的系统调用是阻塞性的,不论是内核的read(FD(0)),write(FD(1))等,都是blocking的操作,导致socket为了不影响其他client的连接,只能new出新线程才异步执行这些blocking操作。
NIO
相信大家在编写Java代码时都听说过NIO,也都清楚IO操作是blocking的,那NIO就是非blocking的。
其实这话对,也不全对。
通过上面对BIO的分析,我们其实可以看到blocking的根本原因是在kernel,所以要实现NIO,根本点也是在kernel。
我们用man命令来看下kernel对socket的描述:
这便是kernel层面的NIO。当然我们Java代码里面也有NIO:
相信大家看了上一章在Java代码中对系统调用的分析,一定可以得出结论:Java代码中的NIO最终是基于kernel的NIO实现的。Java代码的NIO是基于kernel NIO的一层封装。
所以对于NIO,在Java代码中指的是New IO,这是相较于Java中的老的IO方式、
在kernel中,NIO指的是nonBlocking IO,也即非阻塞IO。
代码
现在我们在Java代码中使用NIO方式来实现Sevice:
我们在Java代码中使用NIO方式定义ServerSocket,使用nonBlocking形式accept client,在一个线程中同时负责监听连接和accept连接,并且负责读取数据。
我们实际运行起来看下效果:
我们可以看到在client与service监理连接之后,发送“send message”给service。
service端用一个线程accept了client连接,同时也读取出了发送的数据
我们来看分析下代码,我们一定可以发现这么一行代码:
channel.configureBlocking(false);
我们手动将channel的是否Blocking设置为了false,也就是不阻塞。
追踪
前面我们也已经讲过,想要实现NIO,必须kernel支持,那么kernel是如何做的呢?还是老规矩,我们来追中下service的系统调用:
结合下代码逻辑:
while (true) {
SocketChannel socket = channel.accept();
在主线程的while无限循环中,channel对象一直在尝试accept客户端的连接,上一章节的BIO模型中追中系统调用我们可以发现一旦线程运行到accept处时,线程会一直卡在accept处,一直等到有client连接之后才会往下继续执行,也即accept()方法获取到结果才会往下走。
现在再来看下NIO模式下,kernel是怎么做的:
在没有client建立连接之前,accept()不是一直阻塞在那边,而是直接获得了结果"-1",
在有一个client建立连接之后,accept获取到这个连接,根据socket四要素分配资源创建socket,同时分配描述符FD(16),后续再通过buffer从FD(16)中读取到message。
综述
到这里,我们可以总结下Java中NIO是如何工作的:
与BIO模式不同,NIO模式下只需要一个线程在不停的无限循环,同时处理accept和读取数据、业务处理。
问题
通过NIO模式,我们可以解决BIO模式下为了解决accept Blocking的问题,从而需要创建大量子线程以及维护成本的问题。那NIO是否也有问题呢?
以下讨论基于socket四元组中service的ip,port固定,且service端只是用单线程处理请求:
- 当有大量的client建立socket请求之后,kernel会为每一个socket分配一个FD,并且将该FD与service主线程bind,但是我们需要知道,一个Thread能够分配的FD是有数量限制的,一旦超过了上限,即意味着后续无法处理后续client的数据
- 当同时有大量的client到达,我们用一个Thread去遍历所有已经accept了的FD去获取数据时,每一次循环都是一次系统调用,需要不停的调用kernel的write()和read(),这必然造成用户态和内核态的频繁切换。
这些问题,我们留到后续的多路复用章节再介绍
内容总结
以上是互联网集市为您收集整理的Java NIO模型全部内容,希望文章能够帮你解决Java NIO模型所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。