JavaIO

全面认识 IO

传统的 IO 大致可以分为4种类型:
基于字节操作的 I/O 接口:InputStream 和 OutputStream
基于字符操作的 I/O 接口:Writer 和 Reader
基于磁盘操作的 I/O 接口:File
基于网络操作的 I/O 接口:Socket

java.net 下提供的 Scoket 很多时候人们也把它归为 同步阻塞 IO ,因为网络通讯同样是 IO 行为。
java.io 下的类和接口很多,但大体都是 InputStream、OutputStream、Writer、Reader 的子集,所有掌握这4个类和File的使用,是用好 IO 的关键。

BIO、NIO、AIO的区别

BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式。
NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,基于缓冲实现。
AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的。

Java I/O模型

主要有阻塞IO、非阻塞IO、多路复用IO、异步IO
**阻塞IO:**所谓的阻塞IO就是在读写过程发生阻塞,这个过程是当用户线程发起OI请求时,内核就会去检查数据是否就绪,如果数据还没就绪,则用户线程就会处于阻塞状态,并且会让出cpu。数据就绪后,内核才会复制数据到用户线程并返回,用户才能解除block状态。
优点就是代码比较简单、直观;
缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈

非阻塞IO:当用户线程发起一个read请求后,并不需要等待,而是马上得到一个结果。如果返回结果为error,说明数据还没就绪,用户线程将继续请求read。直到数据就绪后,而且用户线程还在发起read请求,内核才把数据复制到用户线程。也就是说,整个过程中,用户线程都不会让出cpu,当数据还没就绪时,用户线程不断的询问内核数据是否就绪,一直占用cpu。

**多路复用IO:**多路复用IO模型是目前使用的比较多的模型,NIO就是典型的多路复用IO。在多路复用IO模型中,会有一个线程不断的轮询询问socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO操作。多路复用IO好处主要体现在一个线程管理多个socket,而且当真正有读写事件来时才使用IO资源。另一方面,跟非阻塞IO相比它性能更多的原因在于非阻塞IO的轮询询问数据是否就绪是用户线程发起的,而多路复用IO的轮询询问是内核发起的,这个效率会高很多!

**异步IO:**这是最理想的IO模型。用户发起读写操作后,就可以继续干其他事情而不需等待。内核收到异步IO请求后就会立即返回,这就说明read请求成功了。然后内核等待数据就绪后就会把数据复制到用户线程并返回,之后再给用户线程发送一个信号告诉用户线程IO完成了。也就是整个过程,用户线程发起一个读写请求,内核就会异步执行IO,等到用户线程收到完成信号后就可以使用IO了!

IO与NIO的区别

传统IO模型(同步阻塞IO)
在这里插入图片描述
在基于传统同步阻塞模型中:
ServerSocket的主要作用?

1、负责绑定IP地址

2、启动监听端口;

Socket的主要作用?

负责发起连接操作。

连接成功后,双方通过输入输出流进(InputStream/OutputStream)行同步阻塞式通信

通信过程:

1)服务端通常由一个独立的Acceptor线程负责监听客户端的连接;

2)Acceptor监听到客户端的连接请求后,为每个客户端创建一个新的线程进行链路处理;

3)链路处理线程完成客户端请求的处理后,通过输出流返回应答给客户端,然后线程销毁。

模型缺点:

1)服务端线程个数与客户端并发访问连接数是1:1的关系;

2)随着客户端并发访问量增大,服务端线程个数线性膨胀,系统性能急剧下降。

M:N形式的同步阻塞IO通信模型
wKiom1nevqujo8B0AAEIxsiPETk123.png

服务端通过线程池来处理多个客户端的接入请求,通过线程池约束及调配服务端线程资源。

形成客户端个数M:服务端线程池最大线程数N的比例关系

通信过程:

1)当有新的客户端接入时,将客户端Socket封装成一个Task投递到服务端任务队列;

2)服务端任务线程池中的多个线程对任务队列中的Task进行并行处理;

3)任务线程处理完当前Task后,继续从任务队列中取新的Task进行处理。

模型缺点:

1)BIO的读和写操作都是同步阻塞的,阻塞时间取决于对端IO线程的处理速度和网络IO的传输速度,可靠性差;

2)当线程池中所有线程都因对端IO线程处理速度慢导致阻塞时,所有后续接入的客户端连接请求都将在任务队列中排队阻塞堆积;

3)任务队列堆积满后,新的客户端连接请求将被服务端单线程Acceptor阻塞或拒绝,客户端会发生大量连接失败和连接超时。

非阻塞式IO模型(NIO)
NIO模型
多路复用器Selector是NIO模型的基础,一个多路复用器Selector可以同时轮询多个注册在它上面的Channel,服务端只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端连接。

模型优点:

1)NIO中Channel是全双工(是说可以通过Channel 即可完成读操作,也可以完成写操作)的,Channel比流(InputStream/OutputStream)可以更好地映射底层操作系统的API(UNIX网络编程模型中,底层操作系统的通道都是全双工的,同时支持读写操作);

2)客户端发起的连接操作是异步的,不需要像之前的客户端那样被同步阻塞;(此时,客户端不依赖于服务器端,也就是说客户端发完请求可以做其他其他事情)

3)一个Selector线程可以同时处理成千上万个client的请求,而且性能不会随着客户端链接的增加而线性下降;原因:JDK的Selector在Linux等主流操作系统上通过epoll实现,它没有连接句柄数的限制,适合做高性能高负载的网络服务器方案

Reactor模式思想:
分而治之+事件驱动

1)分而治之

一个connection里完整的网络处理过程一般分为6步:accept、read、decode、process、encode、send。

Reactor模式将每个步骤映射为一个Task,服务端线程执行的最小逻辑单元不再是一次完整的网络请求,而是Task,且采用非阻塞方式执行。

2)事件驱动

每个Task对应一个特定事件,当Task准备就绪时,对应的事件通知就会发出。

Reactor收到事件通知后,分发给绑定了对应事件的Handler执行Task。

NIO+单线程Reactor模式
在这里插入图片描述

说明:

Reactor:负责响应事件,将事件分发给绑定了该事件的Handler处理;

Handler:事件处理器,绑定了某类事件,负责执行对应事件的Task对事件进行处理;

Acceptor:就是处理客户端链接connect事件的; Handler的一种,绑定了connect事件。当客户端发起connect请求时,Reactor会将accept事件分发给Acceptor处理。

模型优缺点

a、单线程版本Reactor模型优点是不需要做并发控制,代码实现简单清晰;

b、缺点是不能利用多核CPU,一个线程需要执行处理所有的accept、read、decode、process、encode、send事件,如果其中decode、process、encode事件的处理很耗时,则服务端无法及时响应其他客户端的请求事件。

NIO+多线程Reactor模式

在这里插入图片描述

a、使用线程池执行数据的具体处理过程decode、process、encode,提高数据处理过程的响应速度;

b、Reactor所在单线程只需要专心监听处理客户端请求事件accept、read、write;

此模型缺点:

a、因为Reactor仍是单线程,无法并行响应多个客户端的请求事件(比如同一时刻只能read一个客户端的请求数据)。

NIO+主从多线程Reactor模式
在这里插入图片描述

a、采用多个Reactor,每个Reactor在自己单独线程中执行,可以并行响应多个客户端的请求事件;mainReactor 不再是一个单独的NIO线程,而是一个独立的NIO线程池, 处理链接请求,认证,登陆等操作

Acceptor处理完成后,将事件注册到subReactor线程池中的某个IO线程上去,此IO线程继续完成后面的IO操作

b、Netty采用类似这种模式,boss线程池就是多个mainReactor,worker线程池就是多个subReactor。

总结:

1、对IO模型整体有个认识,为以后使用IO模型积累经验

2、为接下来深入分析Netty中的IO模型积累经验

3、分而治之的思想,为以后做其他框架积累经验

NIO的组成

Buffer:与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的

Channel:表示 IO 源与目标打开的连接,是双向的,但不能直接访问数据,只能与Buffer 进行交互。通过源码可知,FileChannel的read方法和write方法都导致数据复制了两次!

Selector可使一个单独的线程管理多个Channel,open方法可创建Selector,register方法向多路复用器器注册通道,可以监听的事件类型:读、写、连接、accept。注册事件后会产生一个SelectionKey:它表示SelectableChannel 和Selector 之间的注册关系,wakeup方法:使尚未返回的第一个选择操作立即返回,唤醒的原因是:注册了新的channel或者事件;channel关闭,取消注册;优先级更高的事件触发(如定时器事件),希望及时处理。
NIO的服务端建立过程:Selector.open():打开一个Selector;ServerSocketChannel.open():创建服务端的Channel;bind():绑定到某个端口上。并配置非阻塞模式;register():注册Channel和关注的事件到Selector上;select()轮询拿到已经就绪的事件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值