Channel-to-channel传输是可以极其快速的,特别是在底层操作系统提供本地支持的时候。某些操作系统可以不必通过用户空间传递数据而进行直接的数据传输。对于大量的数据传输,这会是一个巨大的帮助。——摘抄至《Java NIO》

  今天学习了NIO的channel-to-channel的数据传输方式,为了对其有一个较清晰的认识,特意进行测试,并与java-io的传统传输方式进行比较。以下将通过对我测试实例的分析来阐述其用法,并挖掘其隐藏问题点。请往下看。

一、先上代码

  1. package nio;
  2.  
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.FileOutputStream;
  7. import java.io.IOException;
  8. import java.nio.channels.FileChannel;
  9. import java.nio.file.Files;
  10. import java.util.Date;
  11.  
  12. /**
  13. * 通道数据迁移
  14. *
  15. * @author wh-simm
  16. *
  17. */
  18. public class ChannelTransfer {
  19.  
  20. public static void main(String[] args) throws Exception {
  21. String srcFile = "E:\\videos\\奇门盾甲.2017.TC720P.国语中字.mp4";// "E:\\spring-mvc.xml";
  22. // catFiles (Channels.newChannel (System.out), srcFile);
  23. nioTest(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-nio.mp4");
  24. nio2Test(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-nio2.mp4");
  25. ioTest(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-io.mp4");
  26. }
  27.  
  28. public static void ioTest(String src, String dest) {
  29. long time = new Date().getTime();
  30. // 传统io处理方式
  31. try {
  32. Files.copy(new File(src).toPath(), new FileOutputStream(new File(dest)));
  33. } catch (FileNotFoundException e) {
  34. // TODO Auto-generated catch block
  35. e.printStackTrace();
  36. } catch (IOException e) {
  37. // TODO Auto-generated catch block
  38. e.printStackTrace();
  39. }
  40. long run = new Date().getTime() - time;
  41. System.out.println("io运行时长:" + run);
  42. }
  43.  
  44. public static void nioTest(String src, String dest) {
  45. long time = new Date().getTime();
  46. try {
  47. catFiles(src, dest);
  48. } catch (Exception e) {
  49. // TODO Auto-generated catch block
  50. e.printStackTrace();
  51. }
  52. long run = new Date().getTime() - time;
  53. System.out.println("nio运行时长:" + run);
  54. }
  55.  
  56. public static void nio2Test(String src, String dest) {
  57. long time = new Date().getTime();
  58. try {
  59. catFiles2(src, dest);
  60. } catch (Exception e) {
  61. // TODO Auto-generated catch block
  62. e.printStackTrace();
  63. }
  64. long run = new Date().getTime() - time;
  65. System.out.println("nio分段传输 运行时长:" + run);
  66. }
  67.  
  68. /**
  69. * nio channel-channel
  70. *
  71. * @param target
  72. * @param src
  73. * @throws Exception
  74. */
  75. private static void catFiles(String src, String dest) throws Exception {
  76. FileInputStream fis = new FileInputStream(src);
  77. FileChannel channel = fis.getChannel();
  78. FileOutputStream fos = new FileOutputStream(dest);
  79. // channel-to-channel 快速传递数据 超过2G的传递会被截断 int.MaxValue
  80. channel.transferTo(0, channel.size(), fos.getChannel());
  81. channel.close();
  82. fos.close();
  83. fis.close();
  84. }
  85. /**
  86. * 定义2G的buffer长度
  87. */
  88. private static final long bufLength = 1024 * 1024 * 1024 * 2L;// 每次拷贝2GB的数据(上限)
  89. /**
  90. * nio channel-channel
  91. *
  92. * @param target
  93. * @param src
  94. * @throws Exception
  95. */
  96. private static void catFiles2(String src, String dest) throws Exception {
  97. FileInputStream fis = new FileInputStream(src);
  98. FileChannel input = fis.getChannel();
  99. // channel-to-channel 快速传递数据 超过2G的传递会被截断 int.MaxValue
  100. FileOutputStream fos = new FileOutputStream(dest);
  101. FileChannel output = fos.getChannel();
  102. long size = input.size();
  103. while (input.position() < size) {
  104. long count = input.transferTo(input.position(), bufLength, output);
  105. input.position(input.position() + count);
  106. }
  107. input.close();
  108. output.close();
  109. fos.close();
  110. fis.close();
  111. }
  112. }

二、测试结果展示

  简要分析:从迁移日志看, 使用nio 不分段迁移方式耗时101095ms,但是有个问题 物理文件只迁移成功了2GB的数据。nio分段迁移,耗时 170428毫秒,物理文件完整迁移。传统io迁移方式耗时206286ms,物理文件完整迁移。针对迁移结果,我们首先要接收到一个信息,channel-to-channel传输时,一次只能最多只能发送2GB的数据。再者就是,nio的channel-to-channel传输大文件的速度确实比传统io方式要快。当然,这个时间偏差,性能比只能反应出在我电脑上的水准,实际上迁移过程中我的磁盘io开销已经爆满了。如果机器性能够优越,这个性能差可能会体现的更明显。

三、源码解析

 3.1、ioTest方法

  传统io,使用stream的一边读一边写的方式来做。这里我调用的是java.nio.file.Files.copy(Path source, OutputStream out)方法,该方法对InputStream往OutputStream中的数据写入过程进行了封装,其定义的缓存尺寸buffer-size,也是比较艺术的,8KB,即系统内存页最小分配单位。假如你还在自己写这个过程,可以考虑直接使用该方法。下面是方法内部源码,仅做参考。

  1. public static long copy(Path source, OutputStream out) throws IOException {
  2. // ensure not null before opening file
  3. Objects.requireNonNull(out);
  4.  
  5. try (InputStream in = newInputStream(source)) {
  6. return copy(in, out);
  7. }
  8. }
  9.  
  10. // buffer size used for reading and writing
  11. private static final int BUFFER_SIZE = 8192;
  12.  
  13. /**
  14. * Reads all bytes from an input stream and writes them to an output stream.
  15. */
  16. private static long copy(InputStream source, OutputStream sink)
  17. throws IOException
  18. {
  19. long nread = 0L;
  20. byte[] buf = new byte[BUFFER_SIZE];
  21. int n;
  22. while ((n = source.read(buf)) > 0) {
  23. sink.write(buf, 0, n);
  24. nread += n;
  25. }
  26. return nread;
  27. }

 3.2、catFiles方法,不分段直接调用transferTo方法

  transferTo( )和transferFrom( )方法允许将一个通道交叉连接到另一个通道,而不需要通过一个中间缓冲区来传递数据。只有FileChannel类有这两个方法,因此channel-to-channel传输中通道之一必须是FileChannel。您不能在socket通道之间直接传输数据,不过socket通道实现WritableByteChannel和ReadableByteChannel接口,因此文件的内容可以用transferTo( )方法传输给一个socket通道,或者也可以用transferFrom( )方法将数据从一个socket通道直接读取到一个文件中。

  直接的通道传输不会更新与某个FileChannel关联的position值。请求的数据传输将从position参数指定的位置开始,传输的字节数不超过count参数的值。实际传输的字节数会由方法返回,可能少于您请求的字节数。

  对于传输数据来源是一个文件的transferTo( )方法,如果position + count的值大于文件的size值,传输会在文件尾的位置终止。假如传输的目的地是一个非阻塞模式的socket通道,那么当发送队列(send queue)满了之后传输就可能终止,并且如果输出队列(output queue)已满的话可能不会发送任何数据。类似地,对于transferFrom( )方法:如果来源src是另外一个FileChannel并且已经到达文件尾,那么传输将提早终止;如果来源src是一个非阻塞socket通道,只有当前处于队列中的数据才会被传输(可能没有数据)。由于网络数据传输的非确定性,阻塞模式的socket也可能会执行部分传输,这取决于操作系统。许多通道实现都是提供它们当前队列中已有的数据而不是等待您请求的全部数据都准备好。

  上述内容摘抄自《Java NIO》,其中提到了输出队列已满的话可能不会发送任何数据。但是没有提及输出队列的上限到底是多少。根据我的测试结果来看,这个上限是2GB,因此超过2GB的文件 不分段是无法完整传输的。这个点很重要,需要记下

3.3、catFiles2方法,分段调用transferTo方法

    这个方法是对catFiles的优化,为了方便对比,写成两个方法。主要就是定义了一个2GB的分段长度,每传输一段数据后,就标记输入通道的position,直到position到达通道的末尾。核心代码:input.position(input.position() + count)

  至此,本次测试分析结束。如果再遇到大文件的拷贝等处理,你可以试试channel-to-channel方式。

传统IO与NIO(channel-to-channel)文件拷贝的探索与性能比对的更多相关文章

  1. 传统IO与NIO的比较

    本文并非Java.io或Java.nio的使用手册,也不是如何使用Java.io与Java.nio的技术文档.这里只是尝试比较这两个包,用最简单的方式突出它们的区别和各自的特性.Java.nio提出了 ...

  2. 传统IO与NIO区别二

    nio是new io的简称,从jdk1.4就被引入了.现在的jdk已经到了1.6了,可以说不是什么新东西了.但其中的一些思想值得我来研究.这两天,我研究了下其中的套接字部分,有一些心得,在此分享.  ...

  3. Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析

    1.  在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区 ...

  4. BIO与NIO的方式实现文件拷贝

    面试题 - 编程实现文件拷贝.(这个题目在笔试的时候经常出现,下面的代码给出了两种实现方案) import java.io.FileInputStream; import java.io.FileOu ...

  5. C# IO操作(四)大文件拷贝(文件流的使用)、文件编码

         大文件拷贝(文件流的使用).文件编码 首先说一下大文件拷贝和文件流,因为计算机的内存资源是有限的,面对几个G甚至更大的文件,需要通过程序来完成拷贝,就需要用到文件流(因为我们无法做到把文件一 ...

  6. File IO(NIO.2):文件操作

    简介 Files类是java.nio.file包的另一个主要入口点.该类提供了一组丰富的静态方法,用于读取,写入和操作文件和目录.Files方法适用于Path对象的实例.在进行其余部分之前,您应该熟悉 ...

  7. Java经典代码片段——使用NIO进行快速的文件拷贝

    public static void fileCopy(File in, File out) throws IOException { FileChannel inChannel = new File ...

  8. netty1---传统IO和NIO的区别

    传统IO: package OIO; import java.io.IOException; import java.io.InputStream; import java.net.ServerSoc ...

  9. NIO之通道(Channel)的原理与获取以及数据传输与内存映射文件

    通道(Channel) 由java.nio.channels包定义的,Channel表示IO源与目标打开的连接,Channel类似于传统的“流”,只不过Channel本身不能直接访问数据,Channe ...

随机推荐

  1. ClassLoader.getResourceAsStream() 与 Class.getResourceAsStream()的区别

        Class.getResourceAsStream() 会指定要加载的资源路径与当前类所在包的路径一致. 例如你写了一个MyTest类在包com.test.mycode 下,那么MyTest. ...

  2. Azure Powershell对ASM资源的基本操作

    本文主要介绍Windows Azure Powershell对ASM资源的基本操作 1.登陆ASM模式,命令:Add-AzureAccount -Environment AzureChinaCloud ...

  3. TP手册学习第一天

    调试执行的sql语句 User::get(1); echo User::getLastSql(); 方法直接返回当前的查询SQL而不执行fetchSql echo User::fetchSql()-& ...

  4. 【转】高精度GPS测量中框架基准的统一

    一.地面基准点的坐标基准转换 一般情况下,我们可以从IERS或IGS等机构获取最新的站坐标和速度场,这些站坐标和速度场是在某一特定基准框架和历元下的坐标值,若要提供高精度GPS网的分析使用,还需要作框 ...

  5. R 调用 python

    上一篇说了python使用 rpy2 调用 R,这里介绍R如何调用python.R的强项在于统计方面,尤其是专业的统计分析,统计检验以及作图功能十分强大,但是在通用性方面,就远不如Python了,比如 ...

  6. Zabbix3.4部署

    Zabbix简介 zabbix(音同 zæbix)是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活 ...

  7. Sql Server的艺术(五) SQL UNION与UNION JOIN运算符

    学习本节所用表: CREATE TABLE TEACHER ( ID INT IDENTITY (,) PRIMARY KEY , --主键,自增长 TNO INT NOT NULL, --教工号 T ...

  8. JavaScript中的类继承

    JavaScript是一个无class的面向对象语言,它使用原型继承而非类继承.这会让那些使用传统面向对象语言如C++和Java的程序员们感到困惑.正如我们所看到的,JavaScript的原型继承比类 ...

  9. JAVA配置&注解方式搭建简单的SpringMVC前后台交互系统

    前面两篇文章介绍了 基于XML方式搭建SpringMVC前后台交互系统的方法,博文链接如下: http://www.cnblogs.com/hunterCecil/p/8252060.html htt ...

  10. VUE2.0 elemenui-ui 2.0.X 封装 省市区三级

    1. 效果图 2. 版本依赖  vue 2.X , elementui  2.0.11  使用element ui  <el-form>标签 3. 源码  components/CityL ...