Java提供了哪几种IO方式
同步、异步、阻塞、非阻塞概念
- 阻塞、非阻塞
IO操作分两部分,发起IO请求和IO数据读写,阻塞、非阻塞主要是针对线程发起IO请求后,是否立即返回 - 同步、异步
针对IO数据读写定义的,读写数据过程不阻塞线程称为异步IO
阻塞IO模型
最传统的一种IO模型,在读写数据的过程中会发生阻塞现象,
当用户线程发出IO请求之后,内核会去查看数据是否准备就绪,如果没有准备就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。
数据准备就绪后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态
比如 java.io 包 ,基于流模型实现,交互方式是同步,阻塞的方式,在读取输入流或写入输出流时候,在读,写动作完成之前,线程会一直阻塞
还有java.net下面提供的部分网络API,比如 Socket、ServerSocket、HttpURLConnection 也归类到同步阻塞 IO 类库,因为网络通信同样是 IO 行为
非阻塞IO模型
当用户线程发起一个read操作后,不需要等待,马上得到了一个结果,如果结果是error时,就表示数据还没有准备好,用户线程可以继续发送read操作,一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,它马上将数据拷贝到用户线程,然后返回
在非阻塞IO模型中用户线程需要不断询问内核数据是否就绪,也就是不会交出CPU,导致CPU占用率很高
多路复用IO模型
会有一个线程不断去轮询多个 socket 的状态,只有当 socket 真正有读写事件时,才真正调用实际的 IO 读写操作
多路复用IO比非阻塞IO模型效率的原因 :
- 在非阻塞IO中不断询问socket状态是通过用户线程去进行的,而在多路复用IO中,轮询每个socket状态是内核进行的
BIO、NIO、AIO
-
BIO
BIO属于阻塞IO模型,当监听到客户端连接请求时,为每个客户端创建一个新的线程进行链路处理,最大的问题是缺乏弹性伸缩能力,当访问量增加以后服务端的线程个数与客户端的并发访问数成1:1的正比关系,当线程数膨胀之后系统的性能将急剧下降,随着并发访问量的继续增大,系统将出现 : 线程堆栈溢出,线程创建失败等问题
-
NIO
NIO主要有三大核心部分:Channel、Buffer、Selector,Channel与流的区别是Channel是双向的
传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer进行操作,数 据总是从Channel读取到Buffer中,或从Buffer写入到Channel中,
Selector用于监听多个通道的时间,因此单个线程可以监听多个数据通道
由于JDK使用poll代替传统的Selector的实现,所以并没有最大连接数的限制,可以接入成千上万的客户端
NIO的非阻塞
使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞 IO 的空闲时间用于在其它通道上执行 IO 操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
-
AIO
AIO是非阻塞异步IO,是连接注册读写事件和回调函数,读写方法异步,并且将结果主动通知程序
可以使用future获取异步的结果
不需要通过多路复用器轮询就可以实现异步读写,简化了NIO的编程模型