BIO、NIO和AIO

一、文件IO详解

IO一般是指Java 文件IO,采用面向流的操作,每次从流中读/写一个或多个字节,直至读/写完所有字节,所以它不能前后移动流中的数据。
IO可以分为文件IO和网络IO,文件IO是中的read和write都是阻塞的,网络IO中的read、write、accept等都是阻塞的。IO流是阻塞的又叫做BIO

二、NIO

1、概念详解

NIO是一种同步非阻塞,主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(多路复用器)。
NIO采用面向缓冲区的操作即(基于Channel和Buffer(缓冲区)进行操作),从流中读/写时先将数据都缓存在缓冲区中,需要时可在缓冲区中前后移动,一个线程请求读/写入字节或者字符流到某通道时(有需要可以进行全双工数据通信的方式)时,所需数据流会被写入缓冲区,线程再从缓存区拿数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,这个线程同就会去做别的事情(非阻塞)。Selector(多路复用器)会监听多个通道的事件(比如:连接打开,数据到达),有数据到了再告诉线程回来处理数据。
NIO分为文件NIO和网络NIO。

2、Selector(选择器)介绍

Selector 一般称 为选择器 ,当然你也可以翻译为 多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。
在这里插入图片描述
使用Selector的好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。

3、NIO源码解析

网络NIO的ServerSocketChannel可以通过configureBlocking方法来设置非阻塞。NIO读写分为准备读写和真正读写,而在准备读写的阶段:将通道中SelectionKey中接收请求事件、读事件、写事件、连接事件这些注册到Selector选择器上,此时每个键对应的通道已经做好了I/O操作的准备(注册这个操作本身是非阻塞的)。
NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。
NIO是采用了Selector选择器的概念,对于前面准备好I/O操作的通道的键。选择器使用select方法进行死循环轮询使用的键SelectionKey找对应的事件和通道。因为select方法是阻塞的并且进行死循环,所以只能是单线程的操作。然后通过SelectionKey中的方法测试通道是否可读可写等操作再进行对缓冲区读写。

三、AIO

JDK1.7发布了NIO2.0,这就是真正意义上的异步非阻塞,服务器的实现模式为多个有效请求一个线程,客户端的IO请求都是由OS先完成再通知服务器应用去启动线程处理(回调)

IO和NIO通俗区别
流和缓冲区对比:流就是每次去购物,一个东西一个东西去拿到收银台付款。而缓冲区就是将每个东西都放到购物车,然后一次拿到收银台付款。

四、阻塞与非阻塞总结

阻塞:线程持续等待资源中数据读/写完成,直到返回响应结果。
非阻塞:线程直接返回结果,不会持续等待资源准备数据结束后才响应结果。

五、同步与异步

同步(Synchronous)与异步(Asynchronous)是指访问数据的机制,或指一次方法的调用或者一次请求的调用。
IO操作举例:
同步一般指主动请求并等待IO操作完成的方式。
异步则指主动请求数据后便可以继续处理其它任务,随后等待IO操作完毕的通知。
老王烧开水:
1、普通水壶煮水,站在旁边,主动的看水开了没有?同步的阻塞
2、普通水壶煮水,去干点别的事,每过一段时间去看看水开了没有,水没开就走人。 同步非阻塞
3、响水壶煮水,站在旁边,不会每过一段时间主动看水开了没有。如果水开了,水壶自动通知他。 异步阻塞
4、响水壶煮水,去干点别的事,如果水开了,水壶自动通知他。异步非阻塞

六、BIO、NIO、AIO适用场景分析:

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。 

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。 

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。 

七、NIO和BIO数据处理

以下内容转载自:https://www.jianshu.com/p/5bb812ca5f8e

使用纯粹的NIO设计相较IO设计,数据处理也受到影响。

在IO设计中,我们从InputStream或 Reader逐字节读取数据。假设你正在处理一基于行的文本数据流,例如:

Name: Anna 
Age: 25
Email: anna@mailserver.com 
Phone: 1234567890 

该文本行的流可以这样处理:

InputStream input = ... ; // get the InputStream from the client socket   

BufferedReader reader = new BufferedReader(new InputStreamReader(input));   

String nameLine   = reader.readLine(); 
String ageLine    = reader.readLine(); 
String emailLine  = reader.readLine(); 
String phoneLine  = reader.readLine();

请注意处理状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道肯定文本行就已读完, readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。 正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则:
在这里插入图片描述
而一个NIO的实现会有所不同,下面是一个简单的例子:

ByteBuffer buffer = ByteBuffer.allocate(48); 
int bytesRead = inChannel.read(buffer); 

注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。假设第一次 read(buffer)调用后,读入缓冲区的数据只有半行,例如,“Name:An”,你能处理数据吗?显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢?好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如:

ByteBuffer buffer = ByteBuffer.allocate(48);   

int bytesRead = inChannel.read(buffer);   

while(! bufferFull(bytesRead) ) {     //是自定义实现方法bufferFull
       bytesRead = inChannel.read(buffer);   
} 

bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区满了。

bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。

如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了“缓冲区数据循环就绪”:
在这里插入图片描述

八、NIO核心类详解

https://www.jianshu.com/p/5bb812ca5f8e

九、NIO、BIO和AIO应用详情场景

待写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值