庖丁解牛:NIO核心概念与机制详解 01

news2024/11/19 0:47:07

文章目录

  • Pre
  • 输入/输出
  • Why NIO
  • 流与块的比较
  • 通道和缓冲区
    • 概述
    • 什么是缓冲区?
    • 缓冲区类型
    • 什么是通道?
    • 通道类型
  • NIO 中的读和写
    • 概述
    • Demo : 从文件中读取
      • 1. 从FileInputStream中获取Channel
      • 2. 创建ByteBuffer缓冲区
      • 3. 将数据从Channle读取到Buffer中
    • Demo : 写入文件
      • 1. 从 FileOutputStream 获取一个通道
      • 2. 创建ByteBuffer缓冲区,写入数据
      • 3. 写入缓冲区
    • Demo : 读写结合
      • Code
      • 内部循环 (inner loop)
      • 【检查状态】
      • 【 重设缓冲区】

在这里插入图片描述


Pre

NIO 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就可以利用低级优化,这是原来的 I/O 包所无法做到的。


输入/输出

I/O 或者输入/输出指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口。它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的。单独的程序一般是让系统为它们完成大部分的工作。

在 Java 编程中,直到最近一直使用 流 的方式完成 I/O。所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节。流 I/O 用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。

NIO 与原来的 I/O 有同样的作用和目的,但是它使用不同的方式 块 I/O。 块 I/O 的效率可以比流 I/O 高许多。


Why NIO

NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义的本机代码。NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。


流与块的比较

原来的 I/O 库(在 java.io.*中) 与 NIO 最重要的区别是数据打包和传输的方式。正如前面提到的,原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。

面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。

一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。


通道和缓冲区

概述

通道 和 缓冲区 是 NIO 中的核心对象,几乎在每一个 I/O 操作中都要使用它们。

  • 通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象。
  • 一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。

什么是缓冲区?

Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。 在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。

在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。

缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程


缓冲区类型

最常用的缓冲区类型是 ByteBuffer。一个 ByteBuffer 可以在其底层字节数组上进行 get/set 操作(即字节的获取和设置)。

ByteBuffer 不是 NIO 中唯一的缓冲区类型。

事实上,对于每一种基本 Java 类型都有一种缓冲区类型:

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
    DoubleBuffer

每一个 Buffer 类都是 Buffer 接口的一个实例。 除了 ByteBuffer,每一个 Buffer 类都有完全一样的操作,只是它们所处理的数据类型不一样。因为大多数标准 I/O 操作都使用 ByteBuffer,所以它具有所有共享的缓冲区操作以及一些特有的操作


什么是通道?

Channel是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流

正如前面提到的,所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节


通道类型

通道与流的不同之处在于**通道是双向的。**而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而 通道 可以用于读、写或者同时用于读写

因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在 UNIX 模型中,底层操作系统通道是双向的。


NIO 中的读和写

概述

读和写是 I/O 的基本过程。从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中。写入也相当简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作。


Demo : 从文件中读取

从一个文件中读取一些数据。如果使用原来的 I/O,那么我们只需创建一个 FileInputStream 并从它那里读取。而在 NIO 中,情况稍有不同:我们首先从 FileInputStream 获取一个 Channel 对象,然后使用这个通道来读取数据。

在 NIO 系统中,任何时候执行一个读操作,都是从通道中读取,但不是直接从通道读取。因为所有数据最终都驻留在缓冲区中,所以您是从通道读到缓冲区中。

因此读取文件涉及三个步骤:

  • (1) 从 FileInputStream 获取 Channel
  • (2) 创建 Buffer
  • (3) 将数据从 Channel 读到 Buffer 中

1. 从FileInputStream中获取Channel

第一步是获取通道。我们从 FileInputStream 获取通道:

FileInputStream fin = new FileInputStream( "readandshow.txt" );
FileChannel fc = fin.getChannel();

2. 创建ByteBuffer缓冲区

下一步是创建缓冲区:

ByteBuffer buffer = ByteBuffer.allocate( 1024 );

3. 将数据从Channle读取到Buffer中

最后,需要将数据从通道读到缓冲区中,如下所示:

fc.read( buffer );

注意:我们不需要告诉通道要读 多少数据 到缓冲区中。每一个缓冲区都有复杂的内部统计机制,它会跟踪已经读了多少数据以及还有多少空间可以容纳更多的数据。更多请继续往下看关于缓冲区内部细节 中介绍更多关于缓冲区统计机制的内容。


Demo : 写入文件

1. 从 FileOutputStream 获取一个通道

在 NIO 中写入文件类似于从文件中读取。首先从 FileOutputStream 获取一个通道:

FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
FileChannel fc = fout.getChannel();

2. 创建ByteBuffer缓冲区,写入数据

下一步是创建一个缓冲区并在其中放入一些数据 。

在这里,数据将从一个名为 message 的数组中取出,这个数组包含字符串 "Some bytes" 的 ASCII 字节(下面会解释 buffer.flip() 和 buffer.put() 调用)。

ByteBuffer buffer = ByteBuffer.allocate( 1024 );
 
for (int ii=0; ii<message.length; ++ii) {
     buffer.put( message[ii] );
}
buffer.flip();

3. 写入缓冲区

最后一步是写入缓冲区中:

fc.write( buffer );

<font color=brown注意在这里同样不需要告诉通道要写入多数据。缓冲区的内部统计机制会跟踪它包含多少数据以及还有多少数据要写入。


Demo : 读写结合

下面我们将看一下在结合读和写时会有什么情况。

我们以一个名为 CopyFile.java 的简单程序作为这个练习的基础,它将一个文件的所有内容拷贝到另一个文件中。CopyFile.java 执行三个基本操作:

  • 首先创建一个 Buffer
  • 然后从源文件中将数据读到这个缓冲区中
  • 然后将缓冲区写入目标文件。

这个程序不断重复 ― 读、写、读、写 ― 直到源文件结束。

CopyFile 程序我们看看如何检查操作的状态,以及如何使用 clear() 和 flip() 方法重设缓冲区,并准备缓冲区以便将新读取的数据写到另一个通道中。

Code

package com.artisan.nio;

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class CopyFile {

    public static  void main( String args[] ) throws Exception {


        // 创建文件输入流和文件输出流
        FileInputStream fin = new FileInputStream( "boot-netty/src/main/resources/a.txt" );
        FileOutputStream fout = new FileOutputStream( "boot-netty/src/main/resources/c.txt" );


        // 创建文件输入流和文件   输出流
        FileChannel fcin = fin.getChannel();
        FileChannel fcout = fout.getChannel();

        // 创建文件输入流和文件输出流
        ByteBuffer buffer = ByteBuffer.allocate( 1024 );

        // 创建文件输入流和文件输出流
        while (true) {
            // 清空缓冲区
            buffer.clear();

            // 清空缓冲区
            int r = fcin.read( buffer );

            // 清空缓冲区
            if (r==-1) {
                break;
            }
            // 反转缓冲区,准备写入数据
            buffer.flip();

            // 将缓冲区的数据写入到文件输出流
            fcout.write( buffer );
        }
    }
}
    

程序解读:

内部循环 (inner loop)

运行 CopyFile 例子 ,因为缓冲区会跟踪它自己的数据,所以 CopyFile 程序的内部循环 (inner loop) 非常简单,如下所示:

fcin.read( buffer );
fcout.write( buffer );

第一行将数据从输入通道 fcin 中读入缓冲区,第二行将这些数据写到输出通道 fcout 。


【检查状态】

下一步是检查拷贝何时完成。当没有更多的数据时,拷贝就算完成,并且可以在 read() 方法返回 -1 是判断这一点,如下所示:

int r = fcin.read( buffer );
 
if (r==-1) {
     break;
}

【 重设缓冲区】

最后,在从输入通道读入缓冲区之前,我们调用 clear() 方法。同样,在将缓冲区写入输出通道之前,我们调用 flip() 方法,如下所示:

buffer.clear();
int r = fcin.read( buffer );
 
if (r==-1) {
     break;
}
 
buffer.flip();
fcout.write( buffer );
  • clear() 方法重设缓冲区,使它可以接受读入的数据。
  • flip() 方法让缓冲区可以将新读入的数据写入另一个通道。

在这里插入图片描述

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

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

相关文章

五、hdfs常见权限问题

1、常见问题 2、案例 &#xff08;1&#xff09;问题 &#xff08;2&#xff09;hdfs的超级管理员 &#xff08;3&#xff09;原因 没有使用Hadoop用户对hdfs文件系统进行操作。 在Hadoop文件系统中&#xff0c;Hadoop用户相当于Linux系统中的root用户&#xff0c;是最高级别用…

win10电脑无法联网,设置IPv4,点击属性无法打开,闪退

win10设置IPv4&#xff0c;点击属性无法打开&#xff0c;闪退 问题:win10设置IPv4&#xff0c;点击属性无法打开&#xff0c;闪退 问题:win10设置IPv4&#xff0c;点击属性无法打开&#xff0c;闪退 第1步&#xff1a;用管理员打开cmd命令窗口&#xff0c;然后输入下面的命令&…

JVM的运行时数据区

Java虚拟机&#xff08;JVM&#xff09;的运行时数据区是程序在运行过程中使用的内存区域&#xff0c;主要包括以下几个部分&#xff1a; 程序计数器虚拟机栈本地方法栈堆方法区运行时常量池直接内存 不同的虚拟机实现可能会略有差异。这些区域协同工作&#xff0c;支持Java…

BUUCTF snake 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 下载附件&#xff0c;解压得到一张snake的图片。 密文&#xff1a; 这里有一张蛇的图片&#xff0c;本人害怕不敢放&#xff0c;想看自己下载附件解压。&#xff08;吐槽一下&#xff0c;我做这道题&#xff0c;全…

每天一点python——day71

#每天一点Python——71 #格式化字符串在Python中&#xff0c;你可以使用格式化字符串来动态地插入变量的值、表达式的结果等到字符串中。 如图&#xff1a;xxx部分需要不断变化&#xff0c;再和原文拼接 如上图所示这是一个类似于字符串拼接的操作。 因为字符串拼接操作会产生很…

【算法挨揍日记】day26——53. 最大子数组和、918. 环形子数组的最大和

53. 最大子数组和 53. 最大子数组和 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 解题思路&#xff1a; 状态…

公司会倒闭,但大模型肯定不会

咋玩抖音的我&#xff0c;前几天在抖音上发了一张图片&#xff0c;没想到竟然有1000多的播放量。 当然这个播放量不算高&#xff0c;甚至在抖音的体系里属于很低的&#xff0c;但是比我预料的可能只有个位数的播放量是高了不少。 这张图片是我用某国产 AI 软件生成的&#xff…

R语言和RStudio的下载安装(非常简便舒适)

目录 R语言和RStudio的关系R语言和Tableau下载R语言进入官网选择清华镜像源Download R for Windows选择base版本开始下载进行安装配置环境变量检查是否安装成功 下载RStudio进入官网点击下载进行安装检查是否安装成功打开选择R语言环境成功打开显示四个工作区 R语言和RStudio的…

CDN是什么,能起到什么作用

随着互联网的快速发展&#xff0c;用户对于快速、稳定、高效的互联网体验的需求日益增长。为了满足这一需求&#xff0c;内容分发网络&#xff08;CDN&#xff09;应运而生&#xff0c;并在近年来得到了广泛应用。CDN通过在全球范围内部署大量的服务器和网络节点&#xff0c;实…

2023 鹏程杯

前言 笔者没有参加此次比赛&#xff0c;由于团队后面会复现此次比赛&#xff0c;所以笔者在此进行复现记录。 silent 考点: 栈溢出 ret2csu 栈迁移 保护: 开了 Full RELRO 和 NX, 禁掉了 execve/execveat 系统调用 漏洞分析 一个裸的栈溢出, 但是没有输出函数可以泄漏 …

MySQL进阶_8.数据库其他调优策略

文章目录 第一节、数据库调优的步骤1.1、选择合适的DBMS1.2、优化表设计1.3、优化逻辑查询1.4、优化物理查询1.5、使用 Redis 或 Memcached 作为缓存1.6、库级优化 第二节、优化MySQL服务器第三节、优化数据库结构 第一节、数据库调优的步骤 1.1、选择合适的DBMS 如果对事务性…

C++基础从0到1入门编程(三)

系统学习C 方便自己日后复习&#xff0c;错误的地方希望积极指正 往期文章&#xff1a; C基础从0到1入门编程&#xff08;一&#xff09; C基础从0到1入门编程&#xff08;二&#xff09; 参考视频&#xff1a; 1.黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难 2.系统…

腾讯云服务器带宽计费模式_按流量和带宽收费说明

腾讯云服务器带宽计费模式分为“按带宽计费”和“按使用流量”两种计费模式&#xff1a;按带宽计费是预付费&#xff0c;一次性购买固定带宽值&#xff0c;先付费&#xff1b;按使用流量计费是先使用后付费&#xff0c;根据云服务器公网出方向实际产生流量来计算。如何选择带宽…

腾讯云CVM服务器标准型/高IO/计算/大数据使用场景及选择说明

腾讯云CVM服务器多种机型使用场景说明&#xff0c;如标准型、内存型、高 IO 型、计算型、大数据型、异构型和批量型&#xff0c;腾讯云百科txybk.com分享不同CVM实例规格配置适合使用场景说明&#xff1a; 腾讯云CVM云服务器 标准型&#xff1a;适合中小型 Web 应用、中小型数据…

腾讯云服务器价格计算器真心好用,推荐给大家!

腾讯云服务器价格计算器可以一键计算出云服务器的精准报价&#xff0c;包括CVM实例规格价格、CPU内存费用、公网带宽收费、存储系统盘和数据盘详细费用&#xff0c;腾讯云百科txybk.com分享腾讯云价格计算器链接入口、使用方法说明&#xff1a; 腾讯云服务器价格计算器 打开腾…

大模型是怎么知道 “我赚了200万” 的?

今天在和 chatGPT 聊天时&#xff0c;我说“我赚了200万”&#xff0c;他立刻就根据这句话给我了一句。 我当然没有赚到200万&#xff0c;只是想引出一个话题&#xff1a;“大模型是如何识别出这句话&#xff0c;又是怎么知道该回答什么的呢&#xff1f;" 在学习自然语言…

Linux(4):Linux文件与目录管理

目录与路径 相对路径在进行软件或软件安装时非常有用&#xff0c;更加方便。利用相对路径的写法必须要确认目前的路径才能正确的去到想要去的目录。 绝对路径的正确度要比相对路径好&#xff0c;因此&#xff0c;在写程序&#xff08;shell scripts&#xff09;来管理系统的条…

苍穹外卖项目笔记(3)——员工管理

前言 这些功能都没有展示对应的测试结果&#xff0c;可自行通过接口文档进行测试&#xff0c;也可以进行前后端联调测试&#xff0c;附代码链接&#xff1a;take-out 1新增员工 1.1 需求分析和设计 产品原型 接口设计 【注】code&#xff1a;操作成功返回1&#xff0c;否则…

腾讯云服务器云硬盘存储三副本消除单点故障

腾讯云服务器造可靠性处于业界领先水平的云服务器&#xff0c;云服务器CVM可靠性单实例99.975%&#xff0c;数据可靠性99.9999999%&#xff0c;云硬盘采用三副本专业存储策略&#xff0c;消除单点故障&#xff0c;保证数据可靠性&#xff0c;腾讯云百科txybk.com分享腾讯云服务…

【数据挖掘 机器学习 | 时间序列】时间序列必备工具箱: 自相关与偏相关检验

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…