Java NIO学习(二):Channel通道

news2024/12/27 16:28:52

2.1 Channel 概述

Java NIO 的通道类似流,但又有些不同:

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。

  • 通道可以异步地读写。

  • 通道中的数据总是要先读到一个 Buffer,或者总是要从一个 Buffer 中写入。

2.2 Channel 实现

下面是 Java NIO 中最重要的Channel 的实现:

  • FileChannel

  • DatagramChannel

  • SocketChannel

  • ServerSocketChannel

  1. FileChannel 从文件中读写数据。

  1. DatagramChannel 能通过 UDP 读写网络中的数据。

  1. SocketChannel 能通过 TCP 读写网络中的数据。

  1. ServerSocketChannel 可以监听新进来的 TCP 连接,像 Web 服务器那样。对每一个新进来的连接都会创建一个 SocketChannel。正如你所看到的,这些通道涵盖了 UDP 和 TCP 网络 IO,以及文件 IO

2.3 FileChannel 介绍和示例

FileChannel 类可以实现常用的 read,write 以及scatter/gather 操作,同时它也提供了很多专用于文件的新方法。这些方法中的许多都是我们所熟悉的文件操作。

下面是一个使用 FileChannel 读取数据到 Buffer 中的示例:

public class FileChannelDemo1 {
    //FileChannel读取数据到buffer中
    public static void main(String[] args) throws Exception {
        //创建FileChannel
        RandomAccessFile aFile = new RandomAccessFile("d:\\atguigu\\01.txt","rw");
        FileChannel channel = aFile.getChannel();

        //创建Buffer
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //读取数据到buffer中
        int bytesRead = channel.read(buf);
        while(bytesRead != -1) {
            System.out.println("读取了:"+bytesRead);
            buf.flip();
            while(buf.hasRemaining()) {
                System.out.println((char)buf.get());
            }
            buf.clear();
            bytesRead = channel.read(buf);
        }
        aFile.close();
        System.out.println("结束了");
    }
}

2.4 FileChannel 操作详解

2.4.1 打开 FileChannel

在使用FileChannel 之前,必须先打开它。但是,我们无法直接打开一个FileChannel,需要通过使用一个 InputStream、OutputStream 或RandomAccessFile来获取一个 FileChannel 实例。下面是通过 RandomAccessFile 打开 FileChannel 的示例:

// 打开FileChannel
RandomAccessFile aFile = new RandomAccessFile("d:\\atguigu\\001.txt","rw");
FileChannel channel = aFile.getChannel();

2.4.2 从FileChannel 读取数据

调用多个read()方法之一从 FileChannel 中读取数据。如:

ByteBuffer buf =ByteBuffer.allocate(48); 
int bytesRead = inChannel.read(buf);

首先,分配一个Buffer。从 FileChannel 中读取的数据将被读到Buffer 中。然后,调用FileChannel.read()方法。该方法将数据从 FileChannel 读取到 Buffer 中。read() 方法返回的 int 值表示了有多少字节被读到了 Buffer 中。如果返回-1,表示到了文件末尾。

2.4.3 向FileChannel 写数据

使用FileChannel.write()方法向 FileChannel 写数据,该方法的参数是一个 Buffer。如:

public class FileChannelDemo2 {

    public static void main(String[] args) throws Exception {
        // 打开FileChannel
        RandomAccessFile aFile = new RandomAccessFile("d:\\atguigu\\001.txt","rw");
        FileChannel channel = aFile.getChannel();

        //创建buffer对象
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        String newData = "data atguigu";
        buffer.clear();

        //写入内容
        buffer.put(newData.getBytes());

        buffer.flip();

        //FileChannel完成最终实现
        while (buffer.hasRemaining()) {
            channel.write(buffer);
        }

        //关闭
        channel.close();
    }
}

注意FileChannel.write()是在 while 循环中调用的。因为无法保证 write()方法一次能向 FileChannel 写入多少字节,因此需要重复调用 write()方法,直到 Buffer 中已经没有尚未写入通道的字节。

2.4.4 关闭 FileChannel

用完FileChannel 后必须将其关闭。如:

inChannel.close();

2.4.5 FileChannel 的 position 方法

有时可能需要在FileChannel 的某个特定位置进行数据的读/写操作。可以通过调用position()方法获取 FileChannel 的当前位置。也可以通过调用 position(longpos)方法设置 FileChannel 的当前位置。这里有两个例子:

long pos = channel.position(); 
channel.position(pos +123);

如果将位置设置在文件结束符之后,然后试图从文件通道中读取数据,读方法将返回1(文件结束标志)。

如果将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置并写入数据。这可能导致“文件空洞”,磁盘上物理文件中写入的数据间有空隙。

2.4.6 FileChannel 的 size 方法

FileChannel 实例的size()方法将返回该实例所关联文件的大小。如:

longfileSize = channel.size();

2.4.7 FileChannel 的 truncate 方法

可以使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度后面的部分将被删除。如:

channel.truncate(1024);

这个例子截取文件的前 1024 个字节。

2.4.8 FileChannel 的 force 方法

FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到 FileChannel 里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。

force()方法有一个 boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。

2.4.9 FileChannel 的 transferTo 和 transferFrom 方法

主要用于通道之间的数据传输:如果两个通道中有一个是 FileChannel,那你可以直接将数据从一个 channel 传输到另外一个 channel。

2.4.9.1 transferFrom()方法

FileChannel 的 transferFrom()方法可以将数据从源通道传输到 FileChannel 中(这个方法在 JDK 文档中的解释为将字节从给定的可读取字节通道传输到此通道的文件中)。下面是一个FileChannel 完成文件间的复制的例子:

//通道之间数据传输
public class FileChannelDemo3 {

    //transferFrom()
    public static void main(String[] args) throws Exception {
        // 创建两个fileChannel
        RandomAccessFile aFile = new RandomAccessFile("d:\\atguigu\\001.txt","rw");
        FileChannel fromChannel = aFile.getChannel();

        RandomAccessFile bFile = new RandomAccessFile("d:\\atguigu\\02.txt","rw");
        FileChannel toChannel = bFile.getChannel();

        //fromChannel 传输到 toChannel
        long position = 0;
        long size = fromChannel.size();
        toChannel.transferFrom(fromChannel,position,size);

        aFile.close();
        bFile.close();
        System.out.println("over!");
    }
}

方法的输入参数 position 表示从 position 处开始向目标文件写入数据,count 表示最多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小于请求的字节数。此外要注意,在SoketChannel 的实现中,SocketChannel 只会传输此刻准备好的数据(可能不足 count 字节)。因此,SocketChannel 可能不会将请求的所有数据(count 个字节)全部传输到FileChannel 中。

2.4.9.2 transferTo()方法

transferTo()方法将数据从FileChannel 传输到其他的 channel 中。

public class FileChannelDemo4 {

    //transferTo()
    public static void main(String[] args) throws Exception {
        // 创建两个fileChannel
        RandomAccessFile aFile = new RandomAccessFile("d:\\atguigu\\001.txt","rw");
        FileChannel fromChannel = aFile.getChannel();

        RandomAccessFile bFile = new RandomAccessFile("d:\\atguigu\\03.txt","rw");
        FileChannel toChannel = bFile.getChannel();

        //fromChannel 传输到 toChannel
        long position = 0;
        long size = fromChannel.size();
        fromChannel.transferTo(0,size,toChannel);

        aFile.close();
        bFile.close();
        System.out.println("over!");
    }
}

2.5 Scatter/Gather

Java NIO 开始支持 scatter/gather,scatter/gather 用于描述从 Channel 中读取或者写入到Channel 的操作。

分散(scatter):从 Channel 中读取是指在读操作时将读取的数据写入多个 buffer 中。因此,Channel 将从 Channel 中读取的数据“分散(scatter)”到多个 Buffer 中。

聚集(gather):写入 Channel 是指在写操作时将多个 buffer 的数据写入同一个 Channel,因此,Channel 将多个 Buffer 中的数据“聚集(gather)”后发送到 Channel。

scatter/ gather 经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的 buffer 中,这样你可以方便的处理消息头和消息体。

2.5.1 Scattering Reads

Scattering Reads 是指数据从一个 channel 读取到多个 buffer 中。

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body    = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);

注意 buffer 首先被插入到数组,然后再将数组作为 channel.read() 的输入参数。 read()方法按照 buffer 在数组中的顺序将从 channel 中读取的数据写入到 buffer,当一个 buffer 被写满后,channel 紧接着向另一个 buffer 中写。

Scattering Reads 在移动下一个 buffer 前,必须填满当前的 buffer,这也意味着它不适用于动态消息(消息大小不固定)。换句话说,如果存在消息头和消息体,消息头必须完成填充(例如 128byte),Scattering Reads 才能正常工作。

2.5.2 Gathering Writes

Gathering Writes 是指数据从多个 buffer 写入到同一个 channel。

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body    = ByteBuffer.allocate(1024);
//writedataintobuffers
ByteBuffer[] bufferArray = { header, body }; channel.write(bufferArray);

buffers 数组是 write()方法的入参,write()方法会按照 buffer 在数组中的顺序,将数据写入到channel,注意只有 position 和 limit 之间的数据才会被写入。因此,如果一个 buffer 的容量为 128byte,但是仅仅包含 58byte 的数据,那么这 58byte 的数据将被写入到 channel 中。因此与 Scattering Reads 相反,Gathering Writes 能较好的处理动态消息。

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

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

相关文章

PrimoBurner SDK for .NET

PrimoBurner SDK for .NET PrimoBurnerSDK是一个CD、DVD和蓝光刻录工具开发工具包。PrimoBurner是跨平台的,它还提供了一个全面灵活的API,用于快速轻松地实现各种燃烧/翻录替代方案。现在PrimoBurner迫使许多卫生、国防、执法、娱乐和其他部门。 PrimoBu…

面试复习题--性能检测原理

1、布局性能检测 Systrace,内存优化工具中也用到了 Systrace,这里关注 Systrace 中的 Frames 页面,正常情况下圆点为绿色,当出现黄色或者红色的圆点时,表现出现了丢帧。 Layout Inspector,是 AndroidStudio 自带工具…

Python环境搭建、Idea整合

1、学python先要下载什么? 2、python官网 3、idea配置Python 4、idea新建python 学python先要下载什么? python是一种语言,首先你需要下载python,有了python环境,你才可以在你的电脑上使用python。现在大多使用的是pyt…

this指针

目录 一、关于this指针的一个经典问题 二、this指针的特点 (一)本质 (二)应用 1. 不使用this指针 2. 使用this指针 3. 两者代码对比 三、代码举例 1. 举例1 2. 举例2 一、关于this指针的一个经典问题 当你进入一个房子…

MySQL面试题:SQL语句的基本语法

MySQL目录一、数据库入门1. 数据管理技术的三个阶段2. 关系型数据库与非关系型数据库3. 四大非关系型数据库a. 基于列的数据库(column-oriented)b. 键值对存储(Key-Value Stores)c. 文档存储(Document Stores&#xff…

In Context Learning 相关分享

个人知乎详见 https://zhuanlan.zhihu.com/p/603650082/edit 1. 前言 随着大模型(GPT3,Instruction GPT,ChatGPT)的横空出世,如何更高效地提示大模型也成了学术界与工业界的关注,因此In-context learning…

ArcGIS API for JavaScript 4.15系列(3)——Dojo中的css样式操作

1、前言 前一篇博客介绍了Dojo中基础的dom操作方法,主要是针对html中的常用标签和属性进行操作。而一个优秀的线上网站自然也离不开css样式的从旁辅助。在实际开发过程中,我们经常会遇到需要动态修改css样式的问题,本文就来介绍一下如何在Do…

大数据框架之Hadoop:入门(一)大数据概论

第1章 大数据概论 1.1大数据概念 大数据(Big Data):指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。 主要解决&#xff…

【java基础】集合

集合 集合类位于java.util 包下,集合类又被称为容器 与数组的区别 数组的长度是固定的,集合的长度是可变的数组可以用来存放基本数据类型,集合存放引用类型,不能存放基本数据类型 如何选择 collection map 黄色的是接口&…

1.计算机基础-操作系统面试题—计算机系统概述、进程管理

本文目录如下:计算机基础-操作系统 面试题一、计算机系统概述CPU 内核态 和 用户态 的区别?从 用户态 切换到 内核态 的常见方法:二、进程管理线程 和 进程 的区别?协程 (goroutine) 和 线程的区别?进程有哪些状态?进…

day11_类中成员之变量

通过以前的学习,我们知道了成员变量是类的重要组成部分。对象的属性以变量形式存在,下面我们就来详解的学习一下类中的变量 成员变量 成员变量的分类 实例变量:没有static修饰,也叫对象属性,属于某个对象的&#xf…

Transformer位置编码图解

在语言中,单词的顺序及其在句子中的位置非常重要。 如果重新排列单词,整个句子的意思可能会发生变化。 在实施 NLP 解决方案时,循环神经网络具有处理序列顺序的内置机制。 然而,transformer 模型不使用递归或卷积,而是…

面试(七)为什么一般希望将析构函数定义为虚函数

class B { public:~B() // 基类析构函数不为虚函数{cout << "B::~B()" << endl;} };class D : public B { public:~D(){cout << "D::~D()" << endl;} };void Test(B* t) {delete t;t nullptr; }int main() {B *pb new B;Test…

TCP/IP网络编程——I/O 复用

完整版文章请参考&#xff1a; TCP/IP网络编程完整版文章 文章目录第 12 章 I/O 复用12.1 基于 I/O 复用的服务器端12.1.1 多进程服务端的缺点和解决方法12.1.2 理解复用12.1.3 复用技术在服务器端的应用12.2 理解 select 函数并实现服务端12.2.1 select 函数的功能和调用顺序1…

anaconda下pytorchCPU GUP安装及问题记录

1 pytorch安装&#xff08;CPU版本&#xff09; pip3 install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple2 torchvision、torchaudio、torchtext安装&#xff1a;解决ModuleNotFoundError: No module named ‘torchvision‘问题 &#xff08…

用“AI“挑选一件智慧礼物

在久违的烟火气回归之际&#xff0c;充满希望的生活可能就从精心挑选一件新年礼物开始。在罗列礼品清单时&#xff0c;你会想到 “数据”也是其中之一吗&#xff1f;事实上&#xff0c;几乎所有时下最受欢迎的带有“智能”一词的设备&#xff0c;都是由大量高质量的数据创建。我…

面试必问的CAS,你懂多少?

目录一.什么是CAS&#xff1f;二.CAS实现过程三.CAS的缺点1.循环时间长2.只能保证一个共享变量是原子操作3.ABA问题和解决方法四.拓展题1.i和i是原子性操作吗&#xff1f;2. i 不加lock和synchronized怎么保证原子性&#xff1f;一.什么是CAS&#xff1f; CAS(Compare And Swa…

uboot源码结构、配置、编译和移植

目录 一、uboot源码结构 1.1 uboot源码获取 1.2 uboot的特点 1.3 uboot源码结构 二、uboot配置与编译 2.1uboot配置 2.2 uboot编译 三、uboot移植 3.1添加board信息 3.2再次配置和编译 3.3添加三星加密引导程序 3.4添加调制代码&#xff08;点灯法&#xff09; 3.…

CMMI-结项管理

结项管理&#xff08;ProjectClosing Management, PCM&#xff09;是指在项目开发工作结束后&#xff0c;对项目的有形资产和无形资产进行清算&#xff1b;对项目进行综合评估&#xff1b;总结经验教训等。结项管理过程域是SPP模型的重要组成部分。本规范阐述了结项管理的规程&…

绘图软件推荐——Diagram Designer

目录 Diagram Designer安装 软件下载 软件图标 Diagram Designer应用 新建页面 工具栏简介 绘制多边形 创建并添加图形模板 图像导出 Diagram Designer安装 软件下载 在腾讯管家&#xff0c;软件管理中 &#xff0c;搜索 Diagram Designer 即可下载软件图标 Diagram Des…