IO流--BIO/NIO/AIO

IO流

java.io

主要分为字符流和字节流,字符流一般用于文本文件,字节流一般用于图像或其他文件。

字符流包括了字符输入流Reader和字符输出流Writer;

字节流包括了字节输入流InputStream和字节输出流OutputStream。

字符流和字节流都有对应的缓冲流,字节流也可以包装为字符流,缓冲流有一个8kb的缓冲数组,可以提高流的读写效率。除了缓冲流外还有过滤流FilterReader、字符数组流CharArrayReader、字节数组流ByteArrayInputStream、文件流FileInputStream。

在这里插入图片描述

同步/异步/阻塞/非阻塞 IO

同步和异步是通信机制,阻塞和非阻塞是调用状态。

  1. 同步和异步是针对应用程序和内核的交互而言的同步/异步是在时间上强调处理事情的结果/机会成本的两种处理策略

    强调结果 意味着对结果的迫不急待,不过结果是正确的还是错误的,反正你要立即给我一个结果响应。(同步

    强调时间机会成本 意味着对等待结果浪费的时间极其难接受,而对结果并不是那么急切,暂时不管结果(让处理方处理完主动通知结果/自己空闲的时候主动去获取结果)转而去处理其他事情。(异步

  2. 阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。

  3. 同步/异步是宏观上(进程间通讯,通常表现为网络IO的处理上),阻塞/非阻塞是微观上(进程内数据传输,通常表现为对本地IO的处理上);阻塞和非阻塞是同步/异步的表现形式

由上描述基本可以总结一句简短的话,**同步和异步是目的,阻塞和非阻塞是实现方式。 **

具体解释:

  • **同步IO:**是用户线程发起IO请求后需要等待或轮询内核IO操作完成后才能继续执行。

    举例:自己上街买衣服,自己亲自干这件事,别的事干不了。

  • **异步IO:**是用户线程发起IO请求后可以继续执行,当内核IO操作完成后会通知用户线程,或调用用户线程的回调函数。

    举例:告诉朋友自己合适衣服的尺寸,大小,颜色,委托朋友去买,然后自己可以去干别的事。(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS)

  • **阻塞IO:**是IO操作需要彻底完成才能返回用户空间。

    举例:去公交站充值,发现这个时候,充值员不在(可能上厕所去了),然后我们就在这里等待,一直等到充值员回来为止。

  • **非阻塞IO:**是IO操作调用后立即返回一个状态值,无需等待IO操作彻底完成。

    举例:银行里取款办业务时,领取一张小票,领取完后我们自己可以玩玩手机,或者与别人聊聊天,当轮我们时,银行的喇叭会通知,这时候我们就可以去了。

以上转自原文:https://blog.csdn.net/u013851082/article/details/53942947

BIO(同步阻塞)

同步阻塞式IO,JDK1.4之前的IO模型。传统的同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。 服务端提供IP和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过套接字进行通信。

简单的描述一下BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理没处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答通宵模型。

传统BIO模型图
在这里插入图片描述

这种服务器实现模式为一个连接请求对应一个线程,服务器需要为每一个客户端请求创建一个线程,如果这个连接不做任何事,会造成不必要的线程开销。可以通过线程池改善,这种IO称为伪异步IO

即当有新的客户端接入时,将客户端的 Socket 封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK 的线程池维护一个消息队列和 N 个活跃线程,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。

伪异步I/O通信框架采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。不过因为它的底层任然是同步阻塞的BIO模型,因此无法从根本上解决问题。

在这里插入图片描述

BIO适用于连接数目少且服务器资源多的场景。


NIO(同步非阻塞)

NIO是JDK1.4引入的同步非阻塞IO。服务器实现模式为多个连接请求对应一个线程,客户端连接请求会注册到一个多路复用器Selector,Selector轮询到连接有IO请求时才启动一个线程处理。

适用于连接数目多且连接时间短的场景。

同步是指线程还是要不断接收客户端连接并处理数据,非阻塞是指如果一个管道没有数,不需要等待,可以轮询下一个管道。

核心组件:

  • **Selector:**多路复用器,轮询检查多个Channel的状态,判断注册事件是否发生,即判断Channel是否可处于可读或可写状态。使用前需要将Channel注册到Selector,注册后会得到一个SelectionKey获取Channel和Selector相关信息。

  • **Channel:**双向通道,替换了BIO中的Stream流,不能直接访问数据,要通过Buffer来读写数据,也可以和其他Channel交互。

  • **Buffer:**缓冲区,本质是一块可读可写数据的内存,来简化数据读写。

    Buffer的三个重要属性:

    • ==position==当写数据到Buffer中时,position表示当前的位置。初始的position值为0。当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的 Buffer 单元。position 最大可为 capacity – 1。 当读取数据时,也是从某个特定位置读。当将 Buffer 从写模式切换到读模式,position会被重置为 0。当从Buffer的 position 处读取数据时,position 向前移动到下一个可读的位置。

    • limit本次读写的极限位置。在写模式下,Buffer的limit表示最多能往 Buffer 里写多少数据。 写模式下,limit 等于 Buffer 的 capacity 。 当切换Buffer到读模式时, limit 表示*最多能读到多少数据*。因此,当切换Buffer到读模式时,limit 会被设置成写模式下的 position 值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是 position )。

    • capacity 最大容量。 只能往里写 capacity 个 byte、long,char 等类型。一旦 Buffer 满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。

      img

    Buffer读写的四个步骤:

    • 写入数据到Buffer

    • 调用==flip()==方法。

      flip()将写转为读,底层实现原理把position置0,并把limit设为当前的position值。

    • 从Buffer中读取数据

    • 调用clear()方法或者compact()方法。

      ==clear()==方法将都转换为写模式(用于读完全数据的情况,把position置0,limit设为capacity)。

      ==compact()==将读转换为写模式(用于存在未读数据的情况,让position指向未读数据的下一个)。

    通道方向和Buffer方向相反,读数据相当于向Buffer写数据,写数据相当于从Buffer读。

在这里插入图片描述

在这里插入图片描述

在别的博主那看到的一个很形象的例子:

连接:https://blog.csdn.net/huangwenyi1010/article/details/75577091

非常形象的实例

小量的线程如何同时为大量连接服务呢,答案就是就绪选择。这就好比到餐厅吃饭,每来一桌客人,都有一个服务员专门为你服务,从你到餐厅到结帐走人,这样方式的好处是服务质量好,一对一的服务,VIP啊,可是缺点也很明显,成本高,如果餐厅生意好,同时来100桌客人,就需要100个服务员,那老板发工资的时候得心痛死了,这就是传统的一个连接一个线程的方式。

老板是什么人啊,精着呢。这老板就得捉摸怎么能用10个服务员同时为100桌客人服务呢,老板就发现,服务员在为客人服务的过程中并不是一直都忙着,客人点完菜,上完菜,吃着的这段时间,服务员就闲下来了,可是这个服务员还是被这桌客人占用着,不能为别的客人服务,用华为领导的话说,就是工作不饱满。那怎么把这段闲着的时间利用起来呢。这餐厅老板就想了一个办法,让一个服务员(前台)专门负责收集客人的需求,登记下来,比如有客人进来了、客人点菜了,客人要结帐了,都先记录下来按顺序排好。每个服务员到这里领一个需求,比如点菜,就拿着菜单帮客人点菜去了。点好菜以后,服务员马上回来,领取下一个需求,继续为别人客人服务去了。这种方式服务质量就不如一对一的服务了,当客人数据很多的时候可能需要等待。但好处也很明显,由于在客人正吃饭着的时候服务员不用闲着了,服务员这个时间内可以为其他客人服务了,原来10个服务员最多同时为10桌客人服务,现在可能为50桌,10客人服务了。

这种服务方式跟传统的区别有两个:

1、增加了一个角色,要有一个专门负责收集客人需求的人。NIO里对应的就是Selector。

2、由阻塞服务方式改为非阻塞服务了,客人吃着的时候服务员不用一直侯在客人旁边了。传统的IO操作,比如read(),当没有数据可读的时候,线程一直阻塞被占用,直到数据到来。NIO中没有数据可读时,read()会立即返回0,线程不会阻塞。

NIO中,客户端创建一个连接后,先要将连接注册到Selector,相当于客人进入餐厅后,告诉前台你要用餐,前台会告诉你你的桌号是几号,然后你就可能到那张桌子坐下了,SelectionKey就是桌号。当某一桌需要服务时,前台就记录哪一桌需要什么服务,比如1号桌要点菜,2号桌要结帐,服务员从前台取一条记录,根据记录提供服务,完了再来取下一条。这样服务的时间就被最有效的利用起来了。

工作原理

在这里插入图片描述

NIO和IO的主要区别

该部分转自:https://blog.csdn.net/zengxiantao1994/article/details/88094910

IONIO
阻塞非阻塞
面向流面向缓冲流
Selector(选择器)

面向流/面向缓冲流

Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。

IO/*面向流*意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。

NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

阻塞/非阻塞

IO的各种流是*阻塞的*。这意味着,当一个线程调用read() 或 write() 时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。

NIO非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

selector

NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。


AIO(异步非阻塞)

AIO是JDK7引入的异步非阻塞IO,被称为NIO.2,IO是对JDK1.4中提出的同步非阻塞I/O(NIO)的进一步增强。服务器实现模式为一个有效请求对应一个线程,客户端的IO请求都是由操作系统先完成IO操作后再通知服务器应用来直接使用准备好的数据。

适用连接数目多且连接时间长的场景。

异步是指服务器端线程接收到客户端管道后就交给底层处理IO通信,可以自己做其他事情;

非阻塞是指客户端有数据才会处理,处理好再通知服务器。

jdk7主要增加了三个新的异步通道:

  • AsynchronousFileChannel: 用于文件异步读写;
  • AsynchronousSocketChannel: 客户端异步socket;
  • AsynchronousServerSocketChannel: 服务器异步socket。

事情;

非阻塞是指客户端有数据才会处理,处理好再通知服务器。

jdk7主要增加了三个新的异步通道:

  • AsynchronousFileChannel: 用于文件异步读写;
  • AsynchronousSocketChannel: 客户端异步socket;
  • AsynchronousServerSocketChannel: 服务器异步socket。

AIO的实现方式包括通过Future的get()方法进行阻塞式调用以及实现CompletionHandler接口,重写请求成功的回调方法completed和请求失败回调方法failed

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值