Java-JVM-4.IO


1️⃣ io原理


📝 同步和异步

名称含义
同步从时间上强调处理事情的结果,强调结果意味着对结果的迫不及待,不管结果如何,反正你要立即给我一个结果响应,一直处于等待状态。
异步调用者发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时调用者在等待结果过程中浪费时间是极其难受的,这个时候我们可以处理其他的请求,被调用者通常依靠事件、回调等机制来通知调用者其返回结果。

📝 阻塞和非阻塞

名称含义
阻塞调用者发起一个请求,会一直等待请求结果返回,也就是当前线程会被挂起,没法做其他事情。
非阻塞调用者发起一个请求,不用一直等待请求结果返回,可以去做其他的事情。
名称含义
同步阻塞我到麻辣烫店点好了东西,然后一直在那等着做好,偶尔还要问下:我的弄好了没。
同步非阻塞我点好东西后,买水果或者买凉菜去了,过了会回来问下:我的弄好了没。
异步阻塞我是在买水果还是在买凉菜的时候,人家老板打电话过来了,说你的麻辣烫已经做好了,你过来拿就好了。
异步非阻塞老板打电话不仅没让我过去拿,还说亲自给我送过来,让我安心的先买水果。

📝 io原理

操作系统与Java基于流的I/O模型有些不匹配。操作系统要移动的是大块数据(缓冲区),这往往是在的协助下完成的。I/O类喜欢操作小块数据——单个字节、几行文本。结果,操作系统送来整缓冲区的数据,java.io的流数据类再花大量时间把它们拆成小块,往往拷贝一个小块就要往返于几层对象。操作系统喜欢整卡车地运来数据,java.io类则喜欢一铲子一铲子地加工数据。
在这里插入图片描述

名称含义
用户态对于操作系统而言,JVM只是一个用户进程(应用程序),处于用户态空间中,而处于用户态空间的进程是不能直接操作底层的硬件(磁盘/网卡)。
系统调用区别于用户进程调用,系统调用时操作系统级别的api,比如java IO的读取数据过程(使用缓冲区),用户程序发起读操作,导致“ syscall read ”系统调用,就会把数据搬入到 一个buffer中;用户发起写操作,导致 “syscall write ”系统调用,将会把一个 buffer 中的数据 搬出去(发送到网络中 or 写入到磁盘文件)
内核态用户态的进程要访问磁盘/网卡(也就是操作IO),必须通过系统调用,从用户态切换到内核态(中断、trap),才能完成。
局部性原理操作系统在访问磁盘时,由于局部性原理,操作系统不会每次只读取一个字节(代价太大),而是借助硬件直接存储器存取(DMA)一次性读取一片(一个或者若干个磁盘块)数据。因此,就需要有一个“中间缓冲区”–即内核缓冲区。先把数据从磁盘读到内核缓冲区中,然后再把数据从内核缓冲区搬到用户缓冲区
从进程看,分用户态和内核态应用程序属于用户态,系统调用则内核态
从内存看,分用户内存和内核内存用户内存 = 应用内存 = JVM内存 = User space
内核内存 = 系统内存 = Kernel space

2️⃣ io模型

名称含义
阻塞IO用户进程阻塞,知道io就绪且io处理(读写)完成,阻塞才解除。
非阻塞IO用户进程不阻塞,需要不断轮询io是否就绪,很占用cpu资源,所以一般不用
多路复用IO用户进程阻塞,也是轮询io是否就绪,但是和非阻塞IO不一样的是,轮询io的是系统进程而非应用进程,实际使用了代理(select/poll/epoll),可以避免cpu空转,所以效率较高,而且1个进程可以管理多个socket,java nio使用的就是多路复用模型。
信号驱动式 I/O(有点类似回调)用户进程不阻塞,而是安装一个信号处理函数,当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。
异步IO用户进程不阻塞,发起io请求(读写)后就可以做自己的事了,io就绪等待和io操作交给操作系统去完成,完成后调用用户进程处理函数,所以整个过程都是异步的。
多路复用IO和非阻塞比较非阻塞是用户进程去轮询io是否就绪,所以用户进程是不阻塞的。
而多路复用,用户进程是阻塞的,轮询io是否就绪是系统进程(内核)在进行。
多路复用IO为何比非阻塞IO模型的效率高是因为在非阻塞IO中,不断地询问socket状态时通过
户线程去进行的,而在多路复用IO中,轮询每个socket状态是内核在进行的,这个效率要比用线程要高的多。

📝 同步IO

在这里插入图片描述


📝 阻塞IO

在这里插入图片描述


📝 非阻塞IO

在这里插入图片描述


📝 异步IO

在这里插入图片描述


3️⃣ io分类

名称含义
BIO同步阻塞I/O。一个连接一个线程,线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。
NIO同步非阻塞I/O。一个请求一个线程。服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。
AIO异步非阻塞I/O(注:JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO2.0也就是AIO)。一个有效请求一个线程。用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。

📝 BIO

BIO通信(一请求一应答)
在这里插入图片描述


📝 NIO

这里需要了解3个概念:缓冲区、通道、多路复用器

名称含义
缓冲区即Buffer,Buffer是一个对象,它包含一些要写入或者要读出的数据,在NIO库中,所有数据读写操作都是用缓冲区处理的。具体一些实现就不说了,通常它是一个字节数组(ByteBuffer)。
通道即Channel,通过它读取和写入数据,就像电话线一样。这里大家是不是有想到流(Stream),区别在于Stream是单向,而Channel是双向的。主要实现呢有以下:FileChannel、DatagramChannel、SocketChannel,通过英文名是不是可以看出个大概意思:文件IO、UDP和TCP
多路复用器即Selector,说白了是用来管理Channel的,Channel注册在Selector中,如果某个Channel上面有TCP连接接入,会被Selector轮询出来,通过相关处理进行后续的I/O操作

在这里插入图片描述
通信步骤

名称含义
服务端通信步骤1.创建ServerSocketChannel实例、并且绑定端口;
2.创建selector实例;
3.将ServerSocketChannel实例注册到选择器上,并监听accept事件;
4.有请求(accept)进来获取客户端的socketChannel的连接,将连接实例注册到选择器上;
5.IO操作(进入缓冲区Buffer);
6.关闭ServerSocketChannel实例
客户端通信步骤1.创建SocketChannel实例和Selector选择器;
2.连接服务器;
3.将SocketChannel注册到selector选择器;
4.发送数据;
5.关闭SocketChannel;

📝 AIO

真正实现了异步IO, 基于windows上IOCP和Linux系统上Epoll 机制实现。底层实现是由操作系统完成的,与NIO不同,当进行读写操作时,只需直接调用AIO.read()或AIO.write()方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。


📝 适用场景

名称含义
BIO适用于连接数目比较小,并且一次发送大量数据的场景;
NIO适用于连接数目多,连接比较短,常用于聊天服务器开发工作(榜上有名的Netty是很大的实践);
AIO适用于连接数目多,连接比较长。这个目前市面上应用还不是很广泛

4️⃣ Java IO(输入输出)介绍

  • IO是Input(输入)和Output(输出)的首字母缩写。
  • I(输入Input):指向Java程序中输入数据,即Java程序从外部获取数据。
  • O(输出Output):指的是Java程序向外部输出数据,即Java程序向外部发送数据。

流:在Java程序和外部之间,数据像水流一样按照顺序传输。Java中,流有两种形式,字节流和字符流,下文会讲解。

外部(也就是数据源)包括:源设备 和 目标设备。
源设备:Java程序使用Input(输入)获取数据的来源。
目标设备:Java程序使用Output(输出)发送数据的目的地

Java IO用在Java程序和外部进行数据交互,Java程序运行在内存中,
要与外部(如:磁盘、网络、数据库等)地方交互数据则需要使用Java IO

5️⃣ 四大IO抽象类

  1. 字节流IO抽象类:InputStream和OutputStream,数据单位为1字节(8Bit)
  • InputStream:字节输入流
常用方法(2个):
int read(); 读取一个字节的数据到程序中,数据类型是int0~255之间的一个数),
如果没有读到数据会返回-1(输入完毕)。如果读到的是字符则返回对应的ASCII码。
void close(); 关闭输入流对象,释放相关资源。
  • OutputStream:字节输出流
常用方法(2个):
void write( int n ); 向目的地写入一个字节 int0~255之间的一个数),没有返回值。
void close(); 关闭输出流对象,释放相关资源。
  1. 字符流IO抽象类:Reader和Writer,数据单位为字符(编码是Unicode,2或4字节)
  • Reader:字符输入流
常用方法(2个):
int read(); 读取一个字符的数据到程序中,数据类型是int0~65535之间的一个数,即Unicode值),
如果没有读到数据会返回-1(输入完毕)。
void close(); 关闭输入流对象,释放相关资源。
  • Writer:字符输出流
常用方法(2个):
void write(int n); 向目的地写入一个字节 int0~65535之间的一个数),没有返回值。
void close(); 关闭输出流对象,释放相关资源。

6️⃣ Java IO体系

Java将四大抽象类的子类们分为了:节点流和处理流,两大类

节点流:直接和数据源相连。
处理流:包装在节点流之上,增加缓存提高效率,提供方法大批量输入输出数据。

在这里插入图片描述

名称含义类型
File非流式IO 用来在Java内存中表示一个文件或目录非流式
RandomAcessFile随机访问流 只能对文件进行随机读写操作的访问非流式
FileInputStream
FileOutputStream
节点流,以字节为单位,直接操作文件。节点流
BufferedInputStream
BufferedOutputStream
处理流,将InputStream/OutputStream对象进行包装,增加缓存功能,提高读写效率处理流
ByteArrayInputStream
ByteArrayOutputStream
节点流,以字节为单位直接操作 字节数组对象节点流
ObjectInputStream
ObjectOutputStream
处理流,以字节为单位直接操作 对象处理流
DataInputStream
DataOutputStream
处理流,以字节为单位直接操作“基本数据类型与字符串类型处理流
InputStreamReader
OutputStreamWriter
处理流,将字节流对象转换为字符流对象处理流
PrintStream处理流,将OutputStream进行包装,可以方便的输出字符,更加灵活处理流
FileReader
FileWriter
节点流,以字节为单位直接对 文本文件 进行读写节点流
BufferedReader
BufferedWriter
处理流,将Reader/Writer对象进行包装,增加缓存功能,提高读写效率处理流

7️⃣ 字节流和字符流


📝 字节流

Java 中的字节流处理的最基本单位为 1 个字节,通常用来处理二进制数据。
字节流类 InputStreamOutputStream 类均为抽象类,代表了基本的输入字节流和输出字节流。

📝 字符流

Java 中的字符流处理的最基本的单元是 Unicode 代码单元(大小2字节),通常用来处理文本数据。

📝 区别

  • 字节流操作的基本单元是字节;字符流操作的基本单元是字符
  • 字节流默认不使用缓冲区;字符流使用缓冲区
  • 字节流通常用于处理二进制数据,不支持直接读写字符;字符流通常用于处理文本数据
  • 在读写文件需要对文本内容进行处理:按行处理、比较特定字符的时候一般会选择字符流;仅仅读写文件,不处理内容,一般选择字节流
  • 我们在使用IO流时通常会选择缓冲流,因为它具有更强的读写能力,可以减少资源浪费和节约时间;但是使用BufferedReader和BufferedWriter时必须将字节流转换为字符流。这也是我们经常会同时使用字节流、字符流和缓冲流的原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yueerba126

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值