前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的DLC—NIO系列(二):Channel

Java中的DLC—NIO系列(二):Channel

作者头像
闲宇非鱼
发布2022-05-25 09:51:14
3440
发布2022-05-25 09:51:14
举报

“人生苦短,不如养狗 作者:Brucebat.Sun ”

一、基本概念

  在上一个章节中我们简单了解到Channel在NIO中的角色和Stream在IO中的角色有些类似,但是特性上却不尽相同。如果说Stream就像生活中的水流一样,那么Channel就如同交通中能够双向行驶的隧道。前者的数据流向固定,也就意味着同一个流只能完成读取或者写入其中一种操作,而后者则能够同时进行读取和写入两种操作。同时在数据传输过程中Channel也对Stream进行了进一步的改进处理,Channel中是通过buffer(也即缓冲区)来进行数据的装载和传输,而Stream中则直接以字节为单位进行数据的传输操作。

  结合上面的描述,我们来对Channel和Stream的特性进行一个简单的对比:

Stream

Channel

是否支持异步

数据传输方式

单工,仅支持读取或写入

全双工,支持同时进行读取和写入操作

是否依赖buffer

否,即按照逐个字节传输

是,即按照逐个buffer传输

  从上面的对比我们不难发现,相比传统IO,NIO在设计思路上更接近于底层操作系统的I/O实现方式,即使用缓冲区的方式来进行数据的批量装载和分组传输,这也是NIO在传输速度上优于传统IO的原因之一。但这种方式也意味着在实际使用Channel时,我们必须要依赖Buffer类来完成对于数据的读写,即如下展示:

代码语言:javascript
复制
读数据:
 channel -> buffer -> program
写数据:
 program -> buffer -> channel

二、基本使用

  点开JDK提供的NIO包我们会看到一长串的Channel类列表,其中最为重要和常见的子类如下:

  从上图中我们不难发现NIO提供了基于处理的数据不同分为两类Channel,一类是针对文件IO的Channel,一类是针对网络IO的Channel。需要注意的是,针对文件IO的Channel不能被设置成阻塞模式,同样也不支持多路复用模式,这一特性也符合操作系统对于纯文件类型数据的IO处理。下面笔者将会基于FileChannel来简单说明一下针对文件IO的Channel的使用。

FileChannel

  NIO类库为我们提供了如下三种方式来获取FileChannel对象:

通过FileChannel自身提供了open方法获取:通过这种方式可以明确指定打开通道的特定操作选项,如标准打开选项、处理符号链接的选项等。示例如下:

代码语言:javascript
复制
FileChannel channel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ);

通过FileInputStream/FileOutputStream获取:通过这种方式获取到的通道只能以读或者写模式打开文件,对应读写模式是继承自对应的Stream对象。示例如下:

代码语言:javascript
复制
FileInputStream inputStream = new FileInputStream(new File("test.txt"));
// 获取一个只读的文件通道
FileChannel inChannel = inputStream.getChannel();

// ============= FileInputStream.getChannel() ======
public FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
              // 这里如果初始化FileInputStream时没有设置channel则使用open方法初始化一个只读通道
              channel = FileChannelImpl.open(fd, path, true, false, this);
            }
            return channel;
        }
}

通过RandomAccessFile获取:通过这种方式获取的通道会继承RandomAccessFile对象设置的读写模式。示例如下:

代码语言:javascript
复制
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
// 获取一个可读可写的文件通道
FileChannel channel = file.getChannel();

// ============= RandomAccessFile.getChannel() ======
public final FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
              // 区别于文件流这里在使用open方法初始化通道时默认是可读的,即当前文件处于只写模式时使用getChannel()获取到的通道也是可读可写的
              channel = FileChannelImpl.open(fd, path, true, rw, this);
            }
            return channel;
        }
}

  在获取FileChannel对象之后,可以根据FileChannel提供的read()或者write()方法对文件进行读取或者写入操作。需要注意的是,上文提到过在实际使用读写方法时需要依赖缓冲区来完成对应,下面我们来分别看一下读数据和写数据的例子:

1. 读数据
代码语言:javascript
复制
FileChannel channel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ);
ByteBuffer buf = ByteBuffer.allocate(5);
while(channel.read(buf)!=-1){
    buf.flip();
    System.out.print(new String(buf.array()));
    buf.clear();
}
channel.close();
2. 写数据
代码语言:javascript
复制
FileChannel channel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.WRITE);
ByteBuffer buf = ByteBuffer.allocate(20);
byte[] data = "Hello, world!".getBytes();
for (int i = 0; i < data.length; ) {
    buf.put(data, i, Math.min(data.length - i, buf.limit() - buf.position()));
    buf.flip();
    i += channel.write(buf);
    buf.compact();
}
channel.force(false);
channel.close();

  需要注意,在上面的实例代码中使用的都是FileChannel原生提供的获取方法,且在进行选项设置的时候只使用了读或者写模式,所以在进行文件的读写操作时都是从文件的头部开始的。当然具体的开始位置还取决于Buffer对象中初始position的设置。

总结

  以上就是Channel类基本概念和针对文件IO的FileChannel基本使用的介绍,在后续的章节中我们会针对网络IO相关的Channel使用进行具体的学习和介绍。

  疫情还在继续,希望大家注意防护,身体健康,保持每天好心情~~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-05-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Brucebat的伪技术鱼塘 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、基本概念
  • 二、基本使用
    • FileChannel
      • 1. 读数据
      • 2. 写数据
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档