概述
IO
IO是输入和输出的缩写. Java的IO包主要关注的是从目标媒介读取数据以及输出数据到目标媒介,目标媒介包括:
文件
管道(线程内部通信)
网络连接
内存缓存
标准输入、输出和错误输出输出操作
输入操作
流
- 流从概念上来说是一个连续的数据流。在Java IO中流既可以是字节流(以字节为单位进行读写),也可以是字符流(以字符为单位进行读写,如Reader/Writer)。你既可以从流中读取数据,也可以往流中写数据。流媒介相关联,原先在目标媒介上的操作都变为对流的操作。InpusStream作为媒介的输入操作的代理, OutputStream作为目标媒介的输出操作的代理,原先在数据源和目标媒介上的操作都转化到了流上的操作。
输出操作
输入操作
套接字
- Socket用于描述IP地址和端口, 是一个通信链的句柄。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。
以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。
从简而论,Socket是作为和更底层的网络设备(网卡)的代理而存在的。
网络编程
IO操作时的目标媒介是套接字的特殊场景,即网络IO特指它的的目标媒介是套接字。它是为不同进程(不限于不同终端间)之间的通信而存在的。由于目标媒介是Socket,它是面向网络的,任何异常的网络状态都会影响到网络IO的操作(包括它的读写速度)
BIO
同步阻塞式IO。BIO是最早的IO操作的API,执行路程如下(每一步操作都是阻塞式的):
Domo:
NIO
- 同步非阻塞式IO。NIO是第二代IO操作的API,它不再让开发人员直接操作Socket,而是增加了加新的概念:Selector、Channel、Buffer,开发人员使用的都是这几个新的对象。
Channel:
- Channel通道是对Socket功能的增强,原先在Socket上的操作在NIO中都变为Channel了。原先在Socket上的操作,都可以在Channel上找到替代的方式。
Channel新增了即时查询与Channel关联的Socket的状态(读取准备就绪、写入准备就绪、可接收新连接、有新连接)
Buffer:
- Buffer缓冲是作为Channel和业务模块之间的中介的角色,NIO的API不提供直接往Channel中读取和写入数据,只能在Buffer和Channel之间读写数据,或者直接和Buffer做读写操作。
SelectionKey
- SelectionKey是Channel上下文对象,他提供Channel关联一个与自己相关的对象,以及对于Channel状态的查询,可以即时查询Channel的状态(读取准备就绪、写入准备就绪、可接收新连接、是否可连接)。
- 我们可以根据SelectionKey来查看Channel是否准备就绪了,如果没有准备就绪则直接跳过它的处理,等到它准备就绪之后再操作,避免了阻塞线程。
Selector:
Selector选择器,NIO的最核心组件,支持多路复用(同时管理多个Channel),使用方法是向Selector中注册Channel并且指定特定的状态,之后Selector.select()方法可以即时返回特定状态的SelectionKey集合(SelectionKey中可以获取Channel)。
NIO在同步执行的情况下,只会对对应状态已经准备就绪的Channel做操作,防止了因为网络IO而让线程完全阻塞的情况:
Demo:
AIO
异步非阻塞IO,是最新的IO操作的API,他对于Channel的读写也是需要通过Buffer来操作的。但是它完全采用异步回调的方式,提供接收客户端Channel、从Channel读取数据、往Channel写入数据的回调时机,所有的流程都是通过监听这些时机来串联起来的:
Demo:
对比
API | BIO | NIO | AIO |
---|---|---|---|
调用方式 | 同步调用 | 同步调用 | 异步调用 |
读写方式 | 单个字节读取,阻塞线程 | 支持非阻塞,可读取字节块 | 非阻塞,可读取字节块 |
效率 | 低 | 高(linux没有性能问题) | 高 |
易用性 | 简单 | 需要自己组织线程模型 | 不需要自己组织线程模型 |
调试难度 | 简单 | 简单 | 难(异步+线程模型) |
第三方支持 | 未知 | Mina、Netty | Talent-aio |