Java NIO学习(二):Channel通道

news/2024/4/20 1:19:11/文章来源:https://blog.csdn.net/m0_49499183/article/details/128954279

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 {//创建FileChannelRandomAccessFile aFile = new RandomAccessFile("d:\\atguigu\\01.txt","rw");FileChannel channel = aFile.getChannel();//创建BufferByteBuffer 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 {// 打开FileChannelRandomAccessFile 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 {// 创建两个fileChannelRandomAccessFile 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 传输到 toChannellong 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 {// 创建两个fileChannelRandomAccessFile 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 传输到 toChannellong 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.luyixian.cn/news_show_255799.aspx

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

相关文章

应用监控以及告警实现

前言 一个Java应用 可以不优秀,但是一定不能没有监控方案。否则极大影响排查线上问题的效 以及系统故障的及时告警 。试想 核心应用挂了一个 但是没有配置告警 理想情况几个小时 被自己人发现了 但是万一自己人也没看到或者没关注 那难道让服务一直挂下去么 &#…

var const let

菜鸟学前端 本文:https://www.jianshu.com/p/b7116525273b 文章目录varlet和const写不动了参考说实话,在看到这个之前,我只知道 var,以前也只用过这玩意。 后面那俩都不知道是干啥用的。 感谢同桌的提示。 记! var v…

四 、QML常用控件的使用详解

在Qt Quick的世界里,window对象用于创建一个与操作系统相关的顶层窗口,而其他的元素,如Text Rectangle,Image等,都睡Windows提功能场景里面的显示对象,Window还有一个派生类,即是大名鼎鼎的Application Win…

ABAP 搜索帮助带出多个字段描述 更新屏幕字段

文章目录需求解析1-DYNP_GET_STEPL2-F4IF_INT_TABLE_VALUE_REQUEST3-获取返回值4-把相应字段更新到内表5-DYNP_VALUES_UPDATE代码需求 如图,当我点击责任工序的搜说帮助时, 同时会把责任人员的描述带出来. 解析 1-DYNP_GET_STEPL 这个方法就是获取当前的循环步骤 2-F4IF_I…

WorkTool无障碍服务实现企业微信机器人接口

前言 想要实现一个企业微信机器人,如京东/拼多多福利群、美团瑞幸定时营销群、自助订单查询、智能咨询或社群管理机器人等,首先官方未提供外部群/客户群的机器人API,会话存档也只在一定场景下适用,及时使用会话存档也存在只能收不…

opencv+python物体检测【03-模仿学习】

仿照练习:原文链接 步骤一:准备图片 正样本集:正样本集为包含“识别物体”的灰度图,一般大于等于2000张,尺寸不能太大,尺寸太大会导致训练时间过长。 负样本集:负样本集为不含“识别物体”的…

微服务--Gateway网关学习

Gateway服务网关 为什么需要网关 网关功能: 身份认证和权限校验服务路由,负载均衡请求限流 网关的技术实现:在SpringCloud网关的实现包括两种: gatewayzuul Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGa…

车道线检测-E2E_LSFitting 论文学习笔记

论文:《End-to-end Lane Detection through Differentiable Least-Squares Fitting》 代码:https://github.com/wvangansbeke/LaneDetection_End2End 材料:https://zhuanlan.zhihu.com/p/94419168 特点: 拟合二次曲线&#xff1b…

专家说年轻人工资低是能力不行….

我们国家的很多专家总讲究语不惊人死不休,同时他们还很喜欢话风高速原地调头。 最近又有一个碉堡了的专家在大放厥词,就在前几天的首届长白山高峰论坛上中航基金副总经理邓海清发表批评年轻人的言论,邓老板是这么说的:很多年轻人…

synchronized 关键字-监视器锁 monitor lock

1.代码示例: package thread3;import java.util.Scanner;public class Test2 {public static Object object new Object();public static void main(String[] args) throws InterruptedException {Thread thread1 new Thread(() -> {Scanner scanner new Sca…

第五十一章 BFS进阶(一)——双端队列广搜

第五十一章 BFS进阶(一)——双端队列广搜一、原理二、例题1、问题2、分析三、代码一、原理 在介绍双端队列广搜之前,我们先回顾一下堆优化版本的dijkstradijkstradijkstra算法。 在这个算法中,我们使用的是小根堆来找到距离起点…

Windows/VM虚拟机安装黑群晖6.1-----保证有效而且简单操作

1视频:Windows/VM虚拟机安装黑群晖教程_哔哩哔哩_bilibili2:网址:Synology Web Assistant3:重新打开群晖操作步骤1:按着视频下载好资源后,按照视频操作,途中修改地方(两个情况选择其中一个&…

Flowable进阶学习(九)数据对象DataObject、租户Tenant、接收任务ReceiveTask

文章目录一、数据对象DataObject二、租户 Tenant三、接收任务 ReceiveTask案例一、数据对象DataObject DataObject可以⽤来定义⼀些流程的全局属性。 绘制流程图,并配置数据对象(不需要选择任意节点) 2. 编码与测试 /*** 部署流程*/ Test…

函数/任意波形发生器 DG5072 技术资料

函数/任意波形发生器 DG5072 DG5000人性化的界面设计和键盘布局,给用户带来非凡体验;丰富的标准配置接口,可轻松实现仪器远程控制,为用户提供更多解决方案。 产品特性 4.3英寸16M真彩TFT液晶显示屏 350 MHz、250MHz、100 MHz或70…

微信卸载后重装的聊天记录还能找回吗?

很多人微信卸载后,问能不能恢复之前的聊天记录? 我想大家肯定都去百度搜索了,能搜出来可行的办法了么,没有是吧,那就看看我能不能帮到你,根据我的经验来解决。 答:理论上是不能的,因…

详细聊聊spring核心思想

犹记我当年初学 Spring 时,还需写一个个 XML 文件,当时心里不知所以然,跟着网上的步骤一个一个配置下来,配错一个看着 error 懵半天,不知所谓地瞎改到最后能跑就行,暗自感叹 tmd 这玩意真复杂。 到后来用上…

C语言入门教程||C语言 循环||C语言 函数

C语言 循环有的时候,可能需要多次执行同一块代码。一般情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。编程语言提供了允许更为复杂的执行路径的多种控制结构。循环语句允许我们多次执…

【Django】云笔记项目

一、介绍 用户可在系统中记录自己的笔记,用户的数据被存储在云笔记平台;用户和用户之间的数据为隔离存储(登陆后才能使用相关笔记功能,且只能查阅自己的笔记) 二、功能拆解 1、用户模块 注册:成为平台…

【Java 面试合集】简述下自定义异常的应用场景

简述下自定义异常的应用场景 1. 概述 如上图所示,我们想回答这个问题就要了解异常的基本结构。哪些是我们可以控制的,哪些是我们不能控制的。 也许有人会问了,其实在逻辑中可以多加判断,为什么要需要自定义呢。 其实判断的内容无…

跳跃游戏 II 解析

题目描述给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处:0 < j < nums[i] i j < n返回到达 nums[n - 1] 的…