IO五种模型详解

一 IO流-输入输出流

1.1 IO是什么

IO就是输入和输出的简称,从计算机组织架构理解,IO就是计算机核心(cpu和内存)与磁盘交互的过程。从磁盘读取数据到内存和把内存数据写回磁盘都是IO操作。应用程序的IO就是指将进程内部数据输出到外部,或将外部数据输入到进程内部

1.2 形式

  • 字节流:操作byte类型数据,主要操作类是OutputStream、InputStream子类,不需要缓冲区,直接对文件进行操作
  • 字符流:操作char类型数据,主要操作类是Writer和Reader的子类,使用缓冲区缓冲字符,不关闭流就不会输出任何内容
  • 字节字符流转换类:OutputStreamWriter,将输出的字符流转化为字节流,InputStreamReader将输入的字节流转换成字符流

1.3 输入流输出流

  • 输入流:输入流是指从进程外部将数据输入到进程内部,例如从磁盘读数据读到内存中
  • 输出流:输出流是指从进程内部将数据输出到外部,例如从内存中读数据读到磁盘中

二 字节流比特流相互转换

2.1 字节流转换比特流

字节流转换为比特流,使用OutputStreamWriter,这里看到使用的是输出流,将字节流对象转化成比特流对象

public static void main(String[] args) throws IOException {
    
    
        File file = new File("D:\\tiger\\JAVA学习笔记\\demo\\src\\main\\java\\com\\mianjing\\test.txt");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file),"UTF-8");
        osw.write("这是一个大西瓜");
        osw.close();
    }

2.2 比特流转换成字节流

比特流对象转化成字节流对象,使用InputStreamReader

public static void main(String[] args) throws IOException {
    
    
        File file = new File("D:\\tiger\\JAVA学习笔记\\demo\\src\\main\\java\\com\\mianjing\\test.txt");
        InputStreamReader isr = new InputStreamReader(new FileInputStream(file),"UTF-8");
        char[] buf = new char[1024];
        int len = isr.read(buf);
        System.out.println(new String(buf,0, len));
        isr.close();
    }

三 同步异步&阻塞非阻塞

要了解常见的五种IO模型,必须搞清楚同步异步,阻塞非阻塞的概念

3.1 同步和异步

同步异步是描述被调用者的,具体概念如下:

  • 同步:事件A调用事件B,事件B立刻执行要做的事,事件A可以得到本次调用的结果
  • 异步:事件A调用事件B,事件B不一定立刻去做,但保证一定会做,A本次调用得不到结果,B做完了会通知A

3.2 阻塞和非阻塞

阻塞和非阻塞是描述调用者的,具体概念如下:

  • 阻塞:事件A调用事件B,在事件B返回给他结果前不做其他事,就这么等着他
  • 非阻塞:事件A调用事件B,事件B返回给他结果前继续做自己的事

3.3 同步异步和阻塞非阻塞

同步异步和阻塞非阻塞没有必然的联系,举个例子来解释一下他们的关系:例如面试中张三(调用方)通知李四(被调用方)面试事宜:

  • 同步阻塞:张三打电话给李四请他准备面试,李四立刻准备,张三等着面试者准备好,两人开始面试
  • 同步非阻塞:张三打电话给面试者请他准备面试,李四立刻准备,张三在他准备期间继续打电话给其他人安排面试,并不断确认这个李四准备好了没有,一旦准备好了就开始面试
  • 异步阻塞:张三打电话给李四请他准备面试,并请李四准备好了给他回个电话,然后就不去联系其他面试者了,就这么等着李四准备,李四先吃完手头的饭,再洗个澡,然后准备面试的事,准备好了回电话给张三,面试开始
  • 异步非阻塞:张三打电话给李四,请李四准备好了给他回个电话,他继续联系其他面试者,李四准备好了给他打电话,面试开始

四 五种IO模型

4.1 阻塞式IO模型

最常见的一种IO模型,在读写会发生阻塞现象。
当用户线程发送IO请求后,内核去查看数据是否就绪,如果没有就绪就等待数据就绪,用户线程此时会进入阻塞状态,交出cpu,等数据就绪好之后,内核将数据拷贝到用户线程,并将结果返回给用户线程,此时线程才会解除阻塞状态
例子:

data = socket.read();

如果数据没有就绪,线程就会一直阻塞在read方法

4.2 非阻塞式IO模型

当一个线程发起read请求时,不需要等待,可以直接得到一个结果,如果结果为error,就不断发起read请求,直到读到为止。
例子:

while(true){
    
    
	data = socket.read();
	if(data != error){
    
    
		break;
	}
}

坏处:
当数据未就绪时,非阻塞式IO不会释放cpu资源,而是一直不停地请求,会导致cpu占用飙高

4.3 IO多路复用模型

  • 概念:IO多路复用模型中会有一个线程通过轮询的方式监听多个socket的状态,只有当某个socket有读写事件时,才会去处理该事件。
  • 优点:因为只有一个线程,不需要为每个socket创建一个新的线程,因此大大减少了资源占用。
  • 缺点:因为只有一个线程轮询,当某个socket有事件传来时则去执行该事件,执行完才继续轮询,那如果事件本身很复杂或者需要处理的时间很长,就容易导致其他事件迟迟得不到处理
  • JAVA NIO:使用了多路复用技术,它是通过selector.select()去查询每个通道是否有事件到达,如果没有则会一直阻塞,因此这种方式会导致程序阻塞

4.4 信号驱动模型

  • 概念:当用户线程需要进行读写操作时,给对应的socket发送一个信号,用户线程会继续执行,当数据就绪时,会给用户线程发一个信号,用户线程接收到信号后就会调用IO读写操作进行读写。可以看成是一种异步IO,实际还是同步IO模型。
  • 用处:可以用在信号产生不是很频繁的地方,因为频繁的信号会使性能大大下降。那就很明显了,可以用在UDP中,因为UDP中只有两个信号:数据报到达套接字和套接字错误信号,那么每次接收到信号后我们很容易就可以判断我们需要做什么。而在TCP中,文件被分割成小的数据包,每个数据包都有校验信号,而且信号几乎没有告诉我们发生了什么事,因此无法使用这种模型

4.5 异步IO模型

  • 概念:当用户线程发送read请求后,可以立刻去做别的事。从内核角度说,当收到asynchronous read之后,会立刻返回,表示read请求已经成功发起。然后内核等数据就绪,就会将数据拷贝到用户线程中,这个过程用户线程完全不用知道IO操作是如何进行的。
  • 异步的体现:除了异步IO模型外,其余的IO模型都是同步的,在上面介绍信号驱动模型时,提到可以看成是一种异步IO,它的机制确实很像,为什么不是异步IO呢?因为无论是多路复用还是信号驱动模型,实际上都是需要用户线程自己去判别事件是否到达或者信号是什么?而不是像异步IO一样,做完了给你打个招呼而已,这里要重新回顾一样同步异步的概念,不能和阻塞和非阻塞混淆

猜你喜欢

转载自blog.csdn.net/weixin_44062399/article/details/123909690