聊聊JavaIO模型那些事

2023年 9月 25日 63.3k 0

本文将重点讲述IO模型,基础相关部分涉及较少,请谨慎食用!

什么是IO?Java中处理IO的方式有什么

IO就是输入与输出,输入数据到内存的过程为输入,从内存输出到外部存储为输出。Java中处理IO的方式如下:

  • 1、字节流:InputStream/OutputStream,java.io.InputStream是所有字节输入流的父级抽象类,而我们常见的像FileInputStream,可以指定文件路径,进行文件读取。这里不涉及使用方法。
  • 2、字符流:Reader/Writer,其实有个知识,不管文件读写还是网络发送数据,信息的最小存储单元都是字节,但是为什么IO操作还是还分字节流和字符流?字符流是Java虚拟机将字节转换过来的,这个过程是比较耗时的,同时如果我们不知道编码的类型,那么很可能会造成读取数据乱码的问题。
  • 3、字节缓冲流:它是字节流的一种增强,典型使用为BufferedInputStream/BufferOutputStream。
  • 4、字符缓冲流:它是字符流的一种增强,典型使用为BufferReader/BufferWriter。
  • 5、打印流:System.out
  • 6、随机访问流:RandomAccessFile,可以支持跳转到文件的任意位置进行读写。同时可以制定模式,r只读,rw读写,rws同时更新文件的内容或元数据(用来描述文件的属性,比如大小,创建时间等)修改到外部存储设备。rwd,同时更新文件的内容到外部存储设备。

关于IO的一些前置知识

在操作系统中,为了保证操作系统的稳定性和安全性,一个进程的地址空间分为了两种,用户空间和内核空间。我们平时运行的程序都是在用户空间运行的,在内核空间中才能完成系统级别的资源操作,比如文件,进程通信,内存管理等等,并且,用户空间不能直接访问内核通奸,必须由系统调用操作才行。
在Unix系统下,IO的模型一共有五种,同步阻塞IO,异步非阻塞IO,IO多路复用,信号驱动IO和异步IO。下面我们说一说Java中常见的三种IO模型。

Java中常见的3中IO模型

BIO(Blocking IO)

同步阻塞IO,当应用程序发起read调用后,会一直阻塞,等内核把数据拷贝到用户空间。

如下图,引自《深入拆解Tomcat & Jetty》

image.png

这种方式有很明显的弊端,当面对大量连接的的时候,效率太低下了。

NIO(Non-blocking/New IO)

在Java1.4的时候引入,提供了Channel,Selector,Buffer等。它并不是同步非阻塞IO,我们先看一下同步非组合IO是什么样的。

如下图引自《深入拆解Tomcat & Jetty》

image.png
同步非阻塞IO进行了一些改进,就是应用程序会一直发起Read调用,也就是会一直问内核,数据准备好没有,不管好没好都会直接返回,但是应用程序可以一直Read,但是数据从内核拷贝到用户空间的这段时间线程依然是阻塞的。同样问题也非常明显,就是,应用程序不断的轮询去read是非常消耗CPU资源的。所以Java的NIO可以看作是IO的多路复用模型。

如下图,引自《深入拆解Tomcat & Jetty》

image.png
在IO多路复用模型中,线程有限发起select调用,询问数据是否准备就绪,等内核准备好了,再发起read调用。一会我们详细说一下NIO,再说下一个模型

AIO(Asynchronous IO)

在Java7中引入了NIO的改进版,它是异步IO模型,基于事件和回调机制实现的,操作之后会理解返回,当后台处理完会通知响应线程进行操作

如下图,引自《深入拆解Tomcat & Jetty》

image.png
但是应用还不广泛。

详细说一说NIO

上面我们说了NIO的一些基础概念,从机制上来说,它是一个非阻塞,面向缓冲,基于通道的IO,可以通过少量线程处理多个连接,很大成都的提高了IO的的效率和并发程度。它最重要的是三个组件

  • 1、Channel:通道,是一个双向的可读可写的数据传输通道,NIO通过Channel来实现数据的输入输出,它可以代表多种连接,比如FileChannel(文件访问通道),SocketChannel,ServerSocketChannel(TCP通信通道),DatagramChannel(UDP)通信通道。
  • 2、Buffer:缓冲区,NIO对数据的读写都是通过缓冲区操作的,读操作的时候将Channel数据填充到Buffer中,写的时候将Buffer中的数据写入到Channel中。
  • 3、Selector:选择器,允许一个线程处理多个Channel,所有的Channel都可以注册到Selector上,由Selector来分配线程处理事件

所以通过描述我们就可以大概知道IO多路复用就像是一个树,Selector就像是一个根节点一样。我们详细说一下这三个组件。

Buffer缓冲区

在阻塞io中,数据的读写都是面向字节流和字符流的。而在BIO中所有的数据都是面向缓冲区的,甭管你是读还是写,对应的都是对缓冲区的数据进行读写。看下图的一个示例

image.png
Buffer是一个抽象类,对于它的子类,比如ByteBuffer,CharBuffer,IntBuffer等存储的数据都是数组格式,对应的就是byte[],char[],int[]。然后我们看一下Buffer定义的四个成员变量。

image.png

  • capacity:容量,Buffer可以存储的最大数据量,创建时设置,不可改变。
  • limit:界限,在写模式下,limit代表最多能写入的数据,一般等于capacity,当然也可以自己设置,读模式下,limit等于Buffer实际写入的数据大小
  • position:位置,下一个可以被读写的数据的索引位置。通过flop归零,从头可以读写
  • mark: 标记,Buffer允许将位置直接定位到该标记,默认设置为-1,就是为了区别0和正整数,代表着一个未设置的状态。

这四种变量的关系:0

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论