FileChannel原理

Posted zhya

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FileChannel原理相关的知识,希望对你有一定的参考价值。

 官方对Channel的解释

 技术分享图片

(一个用于输入/输出操作的连接。通道表示对实体的开放连接,如硬件设备、文件、网络套接字或能够执行一个或多个不同的输入/输出操作的程序组件,例如读取或写入。)

 

Thanking In Java中的描述

技术分享图片

 

Channel是对I/O操作的封装。

FileChannel配合着ByteBuffer,将读写的数据缓存到内存中,然后以批量/缓存的方式read/write,省去了非批量操作时的重复中间操作,操纵大文件时可以显著提高效率(和Stream以byte数组方式有什么区别?经过测试,效率上几乎无区别)。

不过对于运行在容器中的应用需要考虑GC,而ByteBuffer可以使用直接内存(系统内存)(allocateDirect),使用后无需jvm回收。

ByteBuffer还有一个子类MappedByteBuffer可以直接将文件映射到操作系统的虚拟内存,读写文件速度会更快,参考https://www.cnblogs.com/lyftest/p/6564282.html

 

FileChannel和Stream的使用方式和效率对比代码

  1 import java.io.*;
  2 import java.nio.ByteBuffer;
  3 import java.nio.channels.FileChannel;
  4 import java.time.Duration;
  5 import java.time.Instant;
  6 
  7 public class FileChannelTest {
  8 
  9     public static void main(String[] args) {
 10         // 4GB的数据
 11         File sourceFile = new File("d://dd.iso");
 12         File targetFile = new File("d://ee.iso");
 13         targetFile.deleteOnExit();
 14         try {
 15             targetFile.createNewFile();
 16         } catch (IOException e) {
 17             e.printStackTrace();
 18         }
 19 
 20         // stream方式
 21         FileChannelTest.copyFileByStream(sourceFile, targetFile);
 22 
 23         // channel方式
 24 //        FileChannelTest.copyFileByFileChannel(sourceFile, targetFile);
 25     }
 26 
 27     /**
 28      * channel方式
 29      *
 30      * @param sourceFile
 31      * @param targetFile
 32      */
 33     public static void copyFileByFileChannel(File sourceFile, File targetFile) {
 34         Instant begin = Instant.now();
 35 
 36         RandomAccessFile randomAccessSourceFile;
 37         RandomAccessFile randomAccessTargetFile;
 38         try {
 39             // 构造RandomAccessFile,用于获取FileChannel
 40             randomAccessSourceFile = new RandomAccessFile(sourceFile, "r");
 41             randomAccessTargetFile = new RandomAccessFile(targetFile, "rw");
 42         } catch (FileNotFoundException e) {
 43             e.printStackTrace();
 44             return;
 45         }
 46 
 47         FileChannel sourceFileChannel = randomAccessSourceFile.getChannel();
 48         FileChannel targetFileChannel = randomAccessTargetFile.getChannel();
 49 
 50         // 分配1MB的缓存空间
 51         ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);
 52         try {
 53             while (sourceFileChannel.read(byteBuffer) != -1) {
 54                 byteBuffer.flip();
 55                 targetFileChannel.write(byteBuffer);
 56                 byteBuffer.clear();
 57             }
 58         } catch (IOException e) {
 59             e.printStackTrace();
 60         } finally {
 61             try {
 62                 sourceFileChannel.close();
 63             } catch (IOException e) {
 64                 e.printStackTrace();
 65             }
 66             try {
 67                 targetFileChannel.close();
 68             } catch (IOException e) {
 69                 e.printStackTrace();
 70             }
 71         }
 72 
 73         System.out.println("total spent " + Duration.between(begin, Instant.now()).toMillis());
 74     }
 75 
 76     /**
 77      * stream方式
 78      *
 79      * @param sourceFile
 80      * @param targetFile
 81      */
 82     public static void copyFileByStream(File sourceFile, File targetFile) {
 83         Instant begin = Instant.now();
 84 
 85         FileInputStream fis;
 86         FileOutputStream fos;
 87         try {
 88             fis = new FileInputStream(sourceFile);
 89             fos = new FileOutputStream(targetFile);
 90         } catch (FileNotFoundException e) {
 91             e.printStackTrace();
 92             return;
 93         }
 94         // 使用byte数组读取方式,缓存1MB数据
 95         byte[] readed = new byte[1024 * 1024];
 96         try {
 97             while (fis.read(readed) != -1) {
 98                 fos.write(readed);
 99             }
100         } catch (IOException e) {
101             e.printStackTrace();
102         } finally {
103             try {
104                 fos.close();
105             } catch (IOException e) {
106                 e.printStackTrace();
107             }
108             try {
109                 fis.close();
110             } catch (IOException e) {
111                 e.printStackTrace();
112             }
113         }
114 
115         System.out.println("total spent " + Duration.between(begin, Instant.now()).toMillis());
116     }
117 }

 

以上是关于FileChannel原理的主要内容,如果未能解决你的问题,请参考以下文章

Netty——FileChannel中方法说明与代码示例

FileChannel和MappedByteBuffer

使用 FileChannel 和 ByteArrays 读取 ASCII 文件

Linux 上的 FileChannel.write 会产生大量垃圾,但在 Mac 上不会

Java NIO系列教程 FileChannel

FileChannel指南