Java中IO流利用缓冲区的发展过程

在讲缓冲区这个概念前,我们先来了解什么是流。Java中的流按照不同的分类有很多,比如按照流的流向分,可以分为输入流和输出流;按照操作单元划分,可以划分为字节流和字符流;按照流的角色划分为节点流和处理流。

Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

按操作方式分类结构图:
在这里插入图片描述图片来源:Java Guide

按操作对象分类结构图:
在这里插入图片描述
图片来源:Java Guide

通过上述的了解,大家对流有了一个大概的认知了。

流是什么?流是一个 过程 ,一个 动态 的概念。 既然有中间过程,那么就会有起点和终点。比如输入流:起点可以是存储介质,或者内存数组,终点是应用程序
早期版本的JDK是这样实现的,比如我们有一个将文件读取到另外一台服务器的操作。
比如采用FileInputStream
在这里插入图片描述
采用这种方式,每次读取文件都是一次即时操作,多次读取则会造成多次的磁盘IO。
于是Java就提供了一个缓冲相关的类——BufferedInputStream,BufferedInputStream内部含有一个byte数组用作缓冲,可以一次性读取大量数据。减少磁盘IO次数。
在这里插入图片描述
这样的话确实有效减少了磁盘的IO操作,提高了读取效率。但是还是有个问题,上述的过程中其实发生了四次数据拷贝,四次上下文切换。上述其实是BIO的做法(IO操作会堵塞),不利于充分使用CPU和IO。

为了减少这种数据拷贝和上下文切换,于是有了NIO,提供了FileChannel和ByteBuffer,这一部分内存是在堆外分配的,而且FileChannel的transferTo() 方法支持将数据从文件通道传输到给定的可写字节通道。上述的操作过程变成这样。
在这里插入图片描述
这一部分内存是分配在堆外,Java称之为直接内存,这样上下文切换就减少到了两次,而且数据复制也减少到了三次(其中DMA copy 2次,CPU copy 1次)。

在 Linux 内核 2.4 及后期版本中,针对套接字缓冲区描述符做了相应调整,DMA自带了收集功能,对于用户方面,用法还是一样的,但是内部操作已经发生了改变:
在这里插入图片描述

  • 第一步,transferTo() 方法引发 DMA 将文件内容拷贝到内核读取缓冲区。
  • 第二步,把包含数据位置和长度信息的描述符追加到套接字缓冲区,避免了内容整体的拷贝,DMA 引擎直接把数据从内核缓冲区传到协议引擎,从而消除了最后一次 CPU参与的拷贝动作。
    CPU参与的拷贝减少到了0次,上下文切换了两次。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值