以下内容来源自Java 面试指南 | JavaGuide(Java面试 + 学习指南)和自己的学习笔记整理,这里我整理了自己比较感兴趣的点,也有助于我自己理解~
IO 流
什么是IO流
IO 即
Input/Output
,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
InputStream
/Reader
: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。OutputStream
/Writer
: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
著作权归Guide所有 原文链接:https://javaguide.cn/java/io/io-basis.html
按方向分为输入/输出流,按数据单位(处理方式)分为字节流/字符流。
字节流
InputStream
字节输入流
InputStream
用于从源头(通常是文件)读取数据(字节信息)到内存中,java.io.InputStream
抽象类是所有字节输入流的父类。
输入流:将**<存储设备>中的内容读入到<内存>**中。
OutputStream
字节输出流
OutputStream
用于将数据(字节信息)写入到目的地(通常是文件),java.io.OutputStream
抽象类是所有字节输出流的父类。
输出流:将**<内存>中的内容写入到<存储设备>**中。
字符流
Reader
字符输入流
Reader
用于从源头(通常是文件)读取数据(字符信息)到内存中,java.io.Reader
抽象类是所有字符输入流的父类。
Reader
用于读取文本,InputStream
用于读取原始字节。
Writer
字符输出流
Writer
用于将数据(字符信息)写入到目的地(通常是文件),java.io.Writer
抽象类是所有字符输出流的父类。
字节流和字符流区别在于:字节流以字节为单位,可以读写所有类型的数据,字符流以字符为单位,只能读写文本数据。
字节/字符缓冲流
IO 操作是很消耗性能的,缓冲流将数据加载至缓冲区,一次性读取/写入多个字节,从而避免频繁的 IO 操作,提高流的传输效率。
字节缓冲流这里采用了装饰器模式来增强
InputStream
和OutputStream
子类对象的功能。
著作权归Guide所有 原文链接:Java IO 基础知识总结 | JavaGuide(Java面试 + 学习指南)
字符缓冲流和字节缓冲流类似,使用BufferedReader
/BufferedWriter
,它们内部都使用了一个字节数组作为缓冲区。
使用字节缓冲流以实现高效读写
Java两种常见IO模型
BIO (Blocking I/O)
BIO 属于同步阻塞 IO 模型 。
同步阻塞 IO 模型中,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。
图源:《深入拆解Tomcat & Jetty》
在客户端连接数量不高的情况下,是没问题的。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
著作权归Guide所有 原文链接:Java IO 模型详解 | JavaGuide(Java面试 + 学习指南)
NIO (Non-blocking/New I/O)
Java 中的 NIO 于 Java 1.4 中引入,对应
java.nio
包,提供了Channel
,Selector
,Buffer
等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它是支持面向缓冲的,基于通道的 I/O 操作方法。 对于高负载、高并发的(网络)应用,应使用 NIO 。Java 中的 NIO 可以看作是 I/O 多路复用模型。也有很多人认为,Java 中的 NIO 属于同步非阻塞 IO 模型。
跟着我的思路往下看看,相信你会得到答案!
我们先来看看 同步非阻塞 IO 模型。
图源:《深入拆解Tomcat & Jetty》
同步非阻塞 IO 模型中,应用程序会一直发起 read 调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间。
相比于同步阻塞 IO 模型,同步非阻塞 IO 模型确实有了很大改进。通过轮询操作,避免了一直阻塞。
但是,这种 IO 模型同样存在问题:应用程序不断进行 I/O 系统调用轮询数据是否已经准备好的过程是十分消耗 CPU 资源的。
这个时候,I/O 多路复用模型 就上场了。
IO 多路复用模型中,线程首先发起 select 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用。read 调用的过程(数据从内核空间 -> 用户空间)还是阻塞的。
目前支持 IO 多路复用的系统调用,有 select,epoll 等等。select 系统调用,目前几乎在所有的操作系统上都有支持。
- select 调用:内核提供的系统调用,它支持一次查询多个系统调用的可用状态。几乎所有的操作系统都支持。
- epoll 调用:linux 2.6 内核,属于 select 调用的增强版本,优化了 IO 的执行效率。
IO 多路复用模型,通过减少无效的系统调用,减少了对 CPU 资源的消耗。
Java 中的 NIO ,有一个非常重要的选择器 ( Selector ) 的概念,也可以被称为 多路复用器。通过它,只需要一个线程便可以管理多个客户端连接。当客户端数据到了之后,才会为其服务。
著作权归Guide所有 原文链接:Java IO 模型详解 | JavaGuide(Java面试 + 学习指南)
e所有 原文链接:Java IO 模型详解 | JavaGuide(Java面试 + 学习指南)
BIO 属于同步阻塞 IO 模型 ,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间;NIO 属于同步多路复用模型,线程首先发起 select 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用。