不定期更新中……
IO 流的种类
- 按照流的流向,可以分为输入流和输出流;
- 按照操作单元,可以分为字节流和字符流;
- 按照流的角色,可以分为节点流和处理流。
常见的 IO 流
字节流
- FileInputStream
- FileOutputStream
- PipedInputStream
- PipedOutputStream
- ByteArrayInputStream
- ByteArrayOutputStream
- BufferedInputStream
- BufferedOutputStream
- DataInputStream
- DataOutputStream
- ObjectInputStream
- ObjectOutputStream
- SequenceInputStream
- PrintOutputStream
字符流
FileReader
FileWriter
PipedReader
PipedWriter
CharArrayReader
CharArrayWriter
BufferedReader
BufferedWriter
InputStreamReader
OutputStreamWriter
PrintWriter
常见的 IO 类型
BIO
BIO 是指同步阻塞 IO(Blocking I/O)。一次数据的读取或写入会阻塞当前线程,直到本次数据传输结束。操作简单,适合活动连接数较小的情况。
NIO
NIO 是在 Java 1.4 中引入的新的 I/O 模型,因为被称为 New IO。但随着技术的快速发展,NIO 也不再“新”了,因此,我们现在更习惯以它的特性来称其为:同步非阻塞 IO(Non-Blocking I/O)。
NIO 提供了 Channel、Selector、Buffer 等抽象,实现了多路复用。此外,NIO 还提供了 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,分别对应 BIO 中的 Socket 和 ServerSocket。
NIO 并非只是非阻塞的,NIO 同时支持阻塞、非阻塞两种模式,只是因为 NIO 主要就是为了提高 IO 性能而诞生的,所以强调了其核心特性:非阻塞。在日常使用中,我们也更为倾向于 NIO 的非阻塞模式,以获得更高的吞吐量和并发量。
AIO
AIO 是在 Java 7 中引入的异步非阻塞 IO(Asynchronous I/O)。AIO 是基于事件和回调机制实现的,当操作发生后,会直接得到返回,释放 IO 资源,实际操作的执行则交给其他线程来处理,处理完成后通知相应的线程进行后续的操作。
NIO 的组成
缓冲区(Buffer):用来存储待传输的数据,通过 Channel 进行数据传输。
直接缓冲区(DirectByteBuffer):使用堆外内存创建的缓冲区,可以减少一次堆内内存到堆外内存的数据拷贝。
使用堆外内存创建和销毁缓冲区的成本更高且不可控,通常会使用内存池来提高性能。
通道(Channel):用来建立数据传输需要的连接,并传输 Buffer 中的数据。
数据虽然需要通过 Channel 进行传输,但 Channel 是不直接操作数据的,Channel 只负责建立连接并确认传输内容,实际数据的传输是通过
选择器(Selector):用来管理 Channel 和分配
什么是零拷贝
在 Java 程序中,使用 read() 或 write() 方法拷贝,需要在堆内开辟内存空间存储文件流,再从堆内拷贝到堆外,最后从堆外拷贝到操作系统内核,由 DMA 读写到磁盘。期间需要经过两次复制,且用户态和内核态的交互,因此传输效率较慢。
而在操作系统中提供了 mmap() 方法,我们可以在程序中调用该方法,系统会直接在内核开辟内存空间,直接将文件流传输到内核开辟出的内存空间,由 DMA 读写到磁盘。该方法通过减少文件流的拷贝过程和用户态、内核态的交互,从而提高了文件传输的效率。我们把这种方法,称为“零拷贝”。
当然,零拷贝虽然可以提高文件传输效率,但也并非没有缺点的。由于程序直接传入内核内存空间,在发生 IO 异常、宕机等异常情况下,使用零拷贝有可能会导致数据流的丢失。