IO流类型:
- 流操作的颗粒度划分:
- 字节流:以字节为单位,可以操作任何类型的数据,以inputStream和outputStream作为基类
- 字符流:以字符为单位,只能操作字符数据,操作纯文本文件,以Reader和Writer作为基类
- 流的方向划分:
- 输入流
- 输出流
Unix中五种IO模型
- 阻塞IO:应用程序读取缓冲区数据时,当内核没有准备好数据时,应用程序处于等待状态
- 非阻塞IO:当用户空间缓冲区没数据时,返回错误信息给线程。线程采用轮询的方式请求数据
- IO多路复用:应用程序通过select函数,监控多个fd(程序打开的文件),当准备好数据后,再去请求数据。
-
select缺点:
- 监听的IO最大连接数有限,在Linux系统上一般为1024。poll函数解决了连接数限制问题
- select函数返回后,是通过遍历fdset,找到就绪的描述符fd。(仅知道有I/O事件发生,却不知是哪几个流,所以遍历所有流)。epoll解决遍历问题,fd就绪时,内核采用回调机制激活fd,通过epoll_wait()得到通知
-
- 信号驱动模型:程序调用sigaction,向内核发送Sigio信号,当内核准备好数据后,在通过Sigio通知应用
- 异步模型:应用程序向内核发送请求后,立即返回。然后执行准备数据--->复制到用户空间,然后通知应用,数据准备好了
java中的NIO
结构概念图:
主要组件介绍:
- Channel(通道) :
- 类似于传统IO中的流,用于进行数据的读取和写入。通道可以是双向的,例如FileChannel用于文件IO,SocketChannel用于网络IO,等等。
- Buffer(缓冲区) :
- 缓冲区是NIO中用于临时存储数据的对象,可以在通道和应用程序之间传输数据。在读取数据时,数据会先被读入到缓冲区中,然后从缓冲区中进行读取;在写入数据时,数据会先被写入到缓冲区中,然后从缓冲区中写入到通道中。
- Selector(选择器) :
- 选择器是NIO中的核心组件,用于监视多个通道的事件状态,例如读、写、连接等事件。使用选择器可以避免使用多线程处理多个通道,从而提高了系统的性能和可伸缩性。
java代码操作实例:
public static void main(String[] args) throws Exception {
// 打开文件通道
RandomAccessFile file = new RandomAccessFile("example.txt", "r");
FileChannel fileChannel = file.getChannel();
// 创建选择器
Selector selector = Selector.open();
// 注册通道到选择器,并指定感兴趣的事件为读事件
fileChannel.register(selector, SelectionKey.OP_READ);
// 分配缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
// 选择就绪的通道
int readyChannels = selector.select();
if (readyChannels == 0) {
continue;
}
// 获取就绪的SelectionKey集合
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isReadable()) {
// 从通道中读取数据到缓冲区
int bytesRead = fileChannel.read(buffer);
if (bytesRead == -1) {
// 读取完成,关闭通道
fileChannel.close();
return;
}
// 切换缓冲区到读模式
buffer.flip();
// 从缓冲区读取数据
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
//清空缓冲区,准备下一次读取
buffer.clear();
}
// 移除当前的SelectionKey,因为已经处理过了
keyIterator.remove();
}
}
}
}
NIO和IO的区别:
- 阻塞和非阻塞:
- IO会等待内核准备好数据
- NIO是读取数据到缓冲区中,如果内核没准备好数据,就会读取空数据到缓冲区中,是非阻塞的
- 面向对象:
- IO是面向流的,数据是在输入流或输出流中传输。流可以理解为内核和应用程序之间的管道
- NIO面向缓冲区,通道中读取数据到缓冲区,或缓冲区中写入数据到通道
- 方向:
- IO是单向的,输入流或输出流只能进行读或写操作
- NIO是双向的,能在一个通道进行读写操作
- 选择器:
- NIO可以通过选择器,一个线程管理多个通道,与内核进行交互 `