Java NIO_I/O基本概念_Java中的缓冲区(Buffer)_通道(Channel)_网络I/O

I/O基本概念

缓冲区基础

缓冲区是I/O的基础, 进程使用read(), write()将数据读出/写入从缓冲区中; 当缓冲区写满, 内核向磁盘发出指令, 将缓冲区中数据写入磁盘中(这一步不需要CPU), 当磁盘控制器将缓冲区装满, 内核将缓冲区数据拷贝到进程中指定的缓冲区; 操作如下图:

当中忽略了很多细节, 只涉及简单的步骤
 当中忽略了很多细节, 只涉及简单的步骤
上面的进程通常是用户进程, 需要指出的一点就是, 当内核接受到read指令的时候, 首先会去内核内部的缓冲区寻找所需数据, 如果所需的数据不在缓冲区中, 那么用户进程将被挂起, 直到缓冲区中存在数据, 最后内核再将缓冲区中数据拷贝到进程内部的缓冲区中.
注:
1.使用内核的意义:

1. 用户进程不能直接访问磁盘, 需要使用中间层进行访问;
2. 磁盘中的数据总是块状, 而用户需要的数据可能是任意大小的;
3. 内核充当的中间人的角色;

2.读取过程存在的优化方案:

1.进程将所有缓冲区的内存地址交给内核. 进程read操作: 内核根据缓冲区地址, 将数据发散到进程中每个缓冲区的; 进程write操作: 将进程中每个缓冲区的数据集聚, 然后一起存入内核缓冲区; 这样做避免了每一次读写操作都要进行磁盘操作, 减少性能损耗
2.使用虚拟内存. 将内核缓冲区地址与进程缓冲区地址映射到同一片虚拟内存上面, 这样当**磁盘控制器**操控内核缓冲区的时候就等价于操控进程缓冲区, 整个过程避免了拷贝操作这样做的前提是内核和用户的缓冲区必须使用相同的页对齐(固定的大小字节组,一般为512字节大小)具体见下图:
3.使用分页技术进行操作系统I/O:a.确定请求数据在文件系统的哪些磁盘区域, 这些数据可能横跨多个文件系统, 且位置不连续b.内核空间分配足够多的内存页(缓冲区), 用于容纳确定的文件系统页'c.建立内存页与磁盘文件之间的联系, 二者建立映射d.为每一个内存页进行检查e.根据d操作中的检查结果, 决定每个内存页是否执行读写操作f.从磁盘中读取文件内容, 将数据导入, 文件系统对导入数据进行解析

进程与内核的缓冲区共享同一片内存区域
进程与内核的缓冲区共享同一片内存区域

Java中的缓冲区(Buffer)

缓冲区基础

  • 属性:
容量(Capacity): 缓冲区能容纳数据元素的最大量
上界(Limit): 代表缓冲区中最后一个元素的位置, 也代表着缓冲区中元素个数
位置(Position): 下一个被读写位置的索引
标记(Mark): 记录位置
属性范围大小:
0 <= mark <= position <= limit <=capacity
  • 存取
    Buffer内部使用get(),put()函数进行数据存储, get/put当index位置超出范围,抛BufferOverflowException
  • 转置: 使缓冲区的内容逆置, 只需要修改position与limit指向的位置, 让position执行末尾
  • 清空缓冲区: 使用clear(), clear并没有改变缓冲区中元素, 他所改变的就是设定了limit值, 并让其指向0号位置
  • 复制缓冲区内容:
    当缓冲区属性为只读, 那么复制缓冲区内容就是浅复制, 对一个缓冲区的改变会反映到另外一个缓冲区上面, 新的缓冲区将会继承旧的缓冲区所有的属性. 当对只读缓冲区进行put操作, 将抛ReadOnlyBufferException异常
    注:
    如果只读缓冲区与可写缓冲区共享一片内存区域, 可写缓冲区进行改变(如put操作), 这种改变将会体现在只读缓冲区上面; 就所谓的浅复制

字节缓冲区

  • 使用字节缓冲区作为通道执行I/O操作的源和目标, 而向通道中传递一个非ByteBuffer对象的时候将会出现如下的问题:
1.创建一个临时的ByteBuffer对象
2.将目标对象内容复制到临时的ByteBuffer中
3.使用临时ByteBuffer进行I/O操作
4.结束操作, 回收无用数据

上面的过程导致的问题就是严重性能损耗, 当非ByteBuffer对象很多的时候

通道(Channel)

Channel基本概述

通道使用ByteBuffer作为端点, 使文件系统, 进程, 网络等等进行交互, 这种交互总是最小的开销(通道只支持字节操作);
如下图: FileSystem与NetWork的I/O操作是通过Channel执行
在这里插入图片描述

  • 打开通道:
    通道分为2种类型(File, Socket), File: FileChannel, Socket: SocketChannel, ServerSocketChannel, DatagramChannel;
    获得方式:
    1.FileChanne fc = new FileInputStream(new File(PATH)).getChannel();
    //文件有FileInputStream, FileOutputStream, RandomAccessFile
    2.SocketChannel sc = SocketChannel.open();
    其余Socket的Channel同2
    注: 在java.net中的socket使用getChannel获得Channel, 但这样的Channel并不是新通道(它永运不会创建新通道), 只有存在一个与Socket关联的通道, 这样获得的才是新通道, 否则就是一个假通道

  • 使用通道:
    通道间的数据可以是单向的也可以是双向的, 默认的ByteChannel接口实现的是双向数据传输, 但是遇到这样的问题的时候双向传输将会抛异常: FileInputStream的getChannel获得的FileChannel对象是只读的, 但是由于FileInputStream实现了ByteChannel接口, 因此可以调用read, write操作, 当这个管道调用write将会抛未经检查异常NonWritableChannelException;
    通道可以是阻塞, 或非阻塞; 非阻塞: 通道永远不会让调用线程休眠, 请求的操作立即完成, 要么返回获得的数据, 要么返回未获得数据;
    注: 只有sockets, pipes才能使用非阻塞模式

  • 关闭通道:
    使用close()关闭, 关闭通道将会导致底层I/O服务线程暂时阻塞, 即使该通道处于非阻塞模式, close多次调用没有影响(close也会阻塞, 对已经关闭的通道使用close不会产生任何操作, 只会立即返回); 关闭的时候可以使用isOpen()判断通道开放状态. 对于已经关闭的Channel使用读写都将抛CloseChannelException
    注:
    1.当通道实现了InterruptibleChannel接口, 那么当某一线程在该通道上阻塞并且被中断, 那么该通道将被关闭, 被阻塞的线程抛ClosedByInterruptException(在Selectors上阻塞的中断线程不会导致通道关闭); InterruptibleChannel的检查手段是通过isInterrupted()判断线程的interrupt status
    看似上面这种操作过于苛刻, 线程阻塞且中断就关闭对应的Channel, 但这完全是考虑因为操作系统而导致的I/O问题, 增强程序健壮性.
    2.中断的线程可以使用异步关闭, 实现了InterruptibleChannel的线程接口的通道可以在任何时候被关闭, 一个通道关闭的时候在这个通道上的所有阻塞的线程都将被唤醒并接受到一个AsynchronousCloseException
    3.不实现InterruptibleChannel接口的通道通常不进行底层特殊操作, 这些通道永远不会出现阻塞的问题

  • Scatter/Gather
    此处的Scatter/Gather就与缓冲区基础中读取优化中的第2点一样, 在多个缓冲区上实现一个I/O操作.
    Scatter: 对于进程read, 从通道读取的数据会按顺序散布到多个缓冲区, 直到缓冲区或通道中数据用完.
    Gather: 对于进程write, 数据从多个缓冲区按顺序抽取, 然后将数据放入通道中.
    下面使用Gather演示从缓冲区中获取数据写入管道中(Scatter与Gather相反, 就是将管道中数据存入缓冲区):
    缓冲区中元素根据Position, Limit定位每个缓冲区中取到的字符串, 使用标号对应记录缓冲区内容在Channel中顺序
    在这里插入图片描述
    优点: 避免数据的来回拷贝, 可以按照不同的方式组合缓冲区数据的引用
    下面给出演示代码:

package com.demo1;import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class JavaNIOGatherTest{private static final String DEMOGRAPHIC = "c:\\Users\\regotto\\Desktop\\blahblah.txt";public static void main (String [] argv) throws Exception{//默认Buffer10个大小int reps = 10;if (argv.length > 0) {reps = Integer.parseInt (argv [0]);}FileOutputStream fos = new FileOutputStream (DEMOGRAPHIC);GatheringByteChannel gatherChannel = fos.getChannel( );ByteBuffer [] bs = utterBS (reps);//读取到文件末尾, 自动停止writewhile (gatherChannel.write (bs) > 0) {}System.out.println ("Mindshare paradigms synergized to "+ DEMOGRAPHIC);fos.close( );}//模拟缓冲区内容private static String [] col1 = {"Aggregate", "Enable", "Leverage","Facilitate", "Synergize", "Repurpose","Strategize", "Reinvent", "Harness"};private static String [] col2 = {"cross-platform", "best-of-breed", "frictionless","ubiquitous", "extensible", "compelling","mission-critical", "collaborative", "integrated"};private static String [] col3 = {"methodologies", "infomediaries", "platforms","schemas", "mindshare", "paradigms","functionalities", "web services", "infrastructures"};//System.getProperty("line.separator");获取换行private static String newline = System.getProperty ("line.separator");//获取缓冲区内容private static ByteBuffer [] utterBS (int howMany)throws Exception{List list = new LinkedList( );for (int i = 0; i < howMany; i++) {//缓冲区中每一个字符串都随机给一个Position, Limitlist.add (pickRandom (col1, " "));list.add (pickRandom (col2, " "));list.add (pickRandom (col3, newline));}ByteBuffer [] bufs = new ByteBuffer [list.size( )];list.toArray (bufs);return (bufs);}private static Random rand = new Random( );private static ByteBuffer pickRandom (String [] strings, String suffix)throws Exception{String string = strings [rand.nextInt (strings.length)];int total = string.length() + suffix.length( );//为每一个字符串分配对应total容量的ByteBufferByteBuffer buf = ByteBuffer.allocate (total);//编码转换buf.put (string.getBytes ("US-ASCII"));buf.put (suffix.getBytes ("US-ASCII"));buf.flip( );return (buf);}
}运行结果如下(txt文件中存储的内容):
字符串顺序是随机的, 前面代码中使用随机数取Position, Limit
Enable compelling methodologies
Enable frictionless platforms
Facilitate cross-platform paradigms
Reinvent collaborative platforms
Repurpose extensible infomediaries
Strategize integrated paradigms
Strategize ubiquitous platforms
Leverage collaborative infomediaries
Harness collaborative schemas
Harness extensible paradigms

阻塞, 非阻塞; 同步, 异步简述

  • 同步, 异步, 阻塞, 非阻塞简述:
    同步和异步是相对于操作结果来说,会不会等待结果返回。
    阻塞和非阻塞是相对于线程是否被阻塞。

  • 这两者存在本质的区别:
    它们的修饰对象是不同的。阻塞和非阻塞是指进程访问的数据如果尚未就绪,进程是否需要等待,简单说这相当于函数内部的实现区别,也就是未就绪时是直接返回还是等待就绪。
    而同步和异步是指访问数据的机制,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后在读写的时候必须阻塞,异步则指主动请求数据后便可以继续处理其它任务,随后等待I/O,操作完毕的通知,这可以使进程在数据读写时也不阻塞。

网络I/O

  • I/O模型:
    输入操作包含:等待数据; 从内核向进程复制数据; 对于Socket, 先是等待数据从网络到达, 随后将到达的数据复制到内核的缓冲区, 最终将内核缓冲区数据复制到应用进程缓冲区

  • Unix的5种I/O模型

阻塞式IO: 阻塞当前应用, 直到数据从内核缓冲区复制到应用进程缓冲区才返回
非阻塞式IO:内核返回数据错误, 应用程序依旧执行, 但每隔一段时间就要执行系统调用IO是否完成,轮询(polling).当内核有数据时就通知应用等待进行复制(内核复制过程仍具有阻塞)调用更多的底层, CPU利用率低
IO复用(select, poll): 使用select/poll等待数据, 采用多个socket进行等待, 其中一个出现变为可读,就将内核数据复制到进程中(此过程将会出现阻塞状态), 这种处理方式使得当个线程具有多个IO处理能力, 体现了IO的复用,又称为事件驱动IO
信号驱动式IO(SIGIO):应用程序使用sigaction系统调用, 在等待数据阶段应用进程是非阻塞的, 当内核获得数据就向应用进程发送SIGIO信号, 通知程序处理数据的复制过程(此过程将会出现阻塞状态).相比于非阻塞式IO, 信号驱动IO的CPU利用率更高
异步IO:应用程序使用aio_read系统调用, 应用继续执行, 处理其他数据(不存在阻塞状态), 当内核完成所有需要的IO操作, 再通知应用程序可直接获取自身进程缓冲区数据, 也就不存在复制过程的阻塞情况###相对于信号驱动IO, 异步IO的信号处理是在IO已经完成的时候, 而信号驱动是在IO开始的时候

模型图例如下:
在这里插入图片描述

  • 5种IO模型比较
同步IO: 将数据从内核缓冲区复制到应用缓冲区阶段, 应用进程会阻塞阻塞式IO, 非阻塞式IO, IO复用, 信号驱动式IO, 主要区别在于第一阶段: 非阻塞式IO, 信号驱动式IO, 异步IO第一阶段不会阻塞
异步IO: 不会阻塞
  • IO复用:
    包含select/poll/epoll, select出现最早, 然后是poll, 再是epoll
包含select/poll/epoll, select出现最早, 然后是poll, 再是epollselect:int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);readfds, writefds, exceptfds代表读,写,异常条件描述符集合(使用fd_set类型的数组实现,大小为FD_SETSIZE)timeout表示select调用会一直阻塞直到超出timeout, 调用成功返回0, 异常-1, 超时0 poll:int poll(struct pollfd *fds, unsigned int nfds, int timeout);使用链表实现
  • select与poll二者的区别:
    select会修改描述符, poll不会, select描述符使用数组fd_set实现, 默认大小1024, 只能监听1024个描述符, 需要修改FD_SETSIZE, poll没有描述符数量限制

     poll提供更多的事件类型, 描述符重用率比select高一个线程对某个描述符调用select或poll, 另一个线程关闭该描述符, 会导致调用结果不确定二者的速度都慢, 每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区select和poll的返回结果中没有声明哪些描述符已经准备好了, 当返回值大于0, 采用轮询的方式找到IO完成描述符所有系统都支持select, 只有较新的系统支持poll
    
  • epoll:
    int epoll_create(int size);
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
    用于向内核注册新的描述符或改变某个文件描述符的状态, 已注册的描述符使用红黑树维护, 通过回调函数内核会将IO准备好的描述符加入一个链表中管理
    int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);获得事件完成的描述符
    只需要将描述符从进程缓冲区向内核缓冲区拷贝一次, 进程不需要通过轮询的方式来获得事件完成的描述符

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468644.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

跟一个大佬前辈交流了一下

&#xff11;.最近&#xff0c;跟我们公司的测试总监聊天&#xff0c;我随便问了下他几个问题&#xff0c;他也给出了答案&#xff0c;在这里随便聊下&#xff0c;希望给大家的职业生涯中有一些借鉴的作用。也能给新入职场的同学一些方向和指引。2.先介绍下这个技术总监&#x…

LINQ简记(1):基本语法

关于LINQ&#xff08;语言集成查询&#xff09;是.NET 3.5和Visual Studio 2008以上版本中引入的一种有趣的全新概念&#xff0c;语言版本有VB和C#&#xff0c;由于C#与.NET平台结合最为紧密&#xff0c;也是MS当初首推的语言&#xff0c;因此&#xff0c;本系列文章的示例代码…

我认识的一位前辈~

&#xff11;.我最近认识了一个老前辈&#xff0c;关注了我的公众号加了我的好友认识的&#xff0c;我想介绍一下这位前辈&#xff0c;不是因为他有多成功&#xff0c;也不是因为他给了我很多钱&#xff0c;我觉得他是一个在平常不过的人了&#xff0c;因为太过于平凡的思考方式…

机器学习_决策树_ID3算法_C4.5算法_CART算法及各个算法Python实现

下面的有些叙述基于我个人理解, 可能与专业书籍描述不同, 但是最终都是表达同一个意思, 如果有不同意见的小伙伴, 请在评论区留言, 我不胜感激. 参考: 周志华-机器学习 https://blog.csdn.net/xiaohukun/article/details/78112917 https://blog.csdn.net/fuqiuai/article/d…

http数据绑定spring mvc详解

转载于:https://www.cnblogs.com/panxuejun/p/6834365.html

ESP32 入门教学,不入门,不教学

&#xff11;.Internet of things &#xff08;iot&#xff09;这个概念非常火&#xff0c;物联网是什么&#xff1f;把所有物品通过射频识别等信息传感设备与互联网连接起来&#xff0c;实现智能化识别和管理。 物联网通过智能感知、识别技术与普适计算、泛在网络的融合应用&a…

一个从华为离职的朋友

1、我在之前的很多文章里面都谈到了我有一个过硬的华为朋友&#xff0c;我很少去炫耀自己有多厉害&#xff0c;认识了谁谁&#xff0c;但是我非常在意那些跟自己有过交情的朋友&#xff0c;这些朋友不是说你离开了就失去了&#xff0c;也不是你落魄了就不能吹水了&#xff0c;今…

机器学习_简单线性回归与多元回归方程原理推导_处理二值数据_最小二乘法解或梯度下降解多元回归方程(详细推导)以及Python代码实现_回归方程度量方式

下面的有些叙述基于我个人理解, 可能与专业书籍描述不同, 但是最终都是表达同一个意思, 如果有不同意见的小伙伴, 请在评论区留言, 我不胜感激. 参考: 周志华-机器学习 最小二乘法求解多元回归方程: https://blog.csdn.net/weixin_39445556/article/details/83543945 梯度下…

手写Java线程池_超详细解说_绝对能运行_代码超详细注释

线程池 问题背景 只是单纯使用 new Thread(runnable).start(); 的方式创建线程, 将会导致严重的程序性能问题: 1.线程创建, 销毁需要消耗很大的系统资源; 2.虚拟机创建线程的数量是有限的; 2.线程调度切换也将使程序性能下降; 针对这些问题, 对线程数量进行管理, 有效地重复利…

分享一个非常 nice 的工具

最近有个问题&#xff0c;我需要经常使用远程连接工具&#xff0c;原因很简单&#xff0c;我需要控制另外一台电脑&#xff0c;我刚开始使用的是 teamviewer 这个软件&#xff0c;刚开始用的时间是非常爽的&#xff0c;不过有一天他给我来了个提示&#xff0c;说我的软件被商用…

推荐周立功先生的一本书

1. 这篇文章主要是推荐周工的一本书&#xff0c;大家在学习嵌入式的时候&#xff0c;很多人不明白嵌入式系统和单片机的区别&#xff0c;又感觉自己对嵌入式有所了解&#xff0c;知道什么是嵌入式&#xff0c;文章里的很多见解我觉得对很多人都非常有帮助&#xff0c;今晚上周工…

图管够!灌篮高手、女儿国…阿里日_这帮程序员太会玩了!

5月10日是阿里一年一度的阿里日&#xff0c;这对阿里人来说&#xff0c;是个非常特别的日子。 那什么是阿里日呢&#xff1f;看看官方介绍&#xff1a; 它起源于2005年4月20日&#xff0c;是为了纪念2003年5月的“非典”时期阿里人的激情和信念。因此阿里巴巴决定&#xff0c;今…

复习Collection_迭代器使用细节_泛型_综合案例

Collection_迭代器使用细节_泛型_综合案例 主要内容 Collection集合迭代器增强for泛型 第一章 Collection集合 1.1 集合概述 集合&#xff1a;集合是java中提供的一种容器&#xff0c;可以用来存储多个数据。 集合和数组既然都是容器&#xff0c;他们之间的区别: 数组的…

Kubernetes dashboard集成heapster

图形化展示度量指标的实现需要集成k8s的另外一个Addons组件&#xff1a; Heapster 。 Heapster原生支持K8s(v1.0.6及以后版本)和 CoreOS &#xff0c;并且支持多种存储后端&#xff0c;比如&#xff1a; InfluxDB 、 ElasticSearch 、 Kafka 。 下载源包和images文件 下载地址h…

ESP32 分区介绍

软件设计分区表 如上图是正常运行需要的分区&#xff0c;一个系统的正常运行是需要这么多分区的。 分区表&#xff0c;我们软件默认配置的分区表如下 # Espressif ESP32 Partition Table # Name, Type, SubType, Offset, Size, Flags nvs,data,nvs,0x9000,16K, otadata,data,…

python坦克大战_Life is short,you need Python——Python实现坦克大战(一)

先展示一下效果搓搓小手手&#xff0c;坦克大战即将开始……https://www.zhihu.com/video/1140743290784817152一、游戏引擎的安装安装方式有两种&#xff1a;1.pip安装 windows R --> cmd --> pip install pygame2.pycharm安装 File -->setting -->project -->…

复习Java字节流_字符流使用及案例

字节流_字符流 主要内容 IO流字节流字符流异常处理Properties 第一章 IO概述 1.1 什么是IO 生活中&#xff0c;你肯定经历过这样的场景。当你编辑一个文本文件&#xff0c;忘记了ctrls &#xff0c;可能文件就白白编辑了。当你电脑上插入一个U盘&#xff0c;可以把一个视频…

ESP32 coredump 分析

1. 上次写了一个 ESP32 入门&#xff0c;我想有必要再写这篇文章&#xff0c;这次主要是分析 coredump 的&#xff0c;这就像 Android 和 Linux 系统的死机分析&#xff0c;有意思&#xff0c;也有难度。我们写代码的时候&#xff0c;不可避免的会遇到一些 coredump 的问题&am…

我那个在华为过得很好的朋友

最近华为的事件又上了热搜&#xff0c;不管是微信还是知乎&#xff0c;都在啃这个热点&#xff0c;一种是看热闹不闲事大的&#xff0c;一种是看热闹闲事大的&#xff0c;我呢&#xff1f;昨晚跟我的一个华为朋友玩了几把王者荣耀&#xff0c;虽然不能带他全盘浪&#xff0c;但…

Redis概述_使用命令对redis的数据进行增删改查_Jedis连接redis进行数据操作_redis进行数据缓存案例

学习目标 redis 概念下载安装命令操作 1. 数据结构持久化操作使用Java客户端操作redis Redis 前言(从百度上抄的, 看看了解一下, 懒得排版了) 1. 概念&#xff1a; redis是一款高性能的NOSQL系列的非关系型数据库1.1.什么是NOSQLNoSQL(NoSQL Not Only SQL)&#xff0c;意即…