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

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

一、先上代码

package nio;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.util.Date; /**
* 通道数据迁移
*
* @author wh-simm
*
*/
public class ChannelTransfer { public static void main(String[] args) throws Exception {
String srcFile = "E:\\videos\\奇门盾甲.2017.TC720P.国语中字.mp4";// "E:\\spring-mvc.xml";
// catFiles (Channels.newChannel (System.out), srcFile);
nioTest(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-nio.mp4");
nio2Test(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-nio2.mp4");
ioTest(srcFile, "E:\\videos\\奇门盾甲.2017.TC720P.国语中字-copy-io.mp4");
} public static void ioTest(String src, String dest) {
long time = new Date().getTime();
// 传统io处理方式
try {
Files.copy(new File(src).toPath(), new FileOutputStream(new File(dest)));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long run = new Date().getTime() - time;
System.out.println("io运行时长:" + run);
} public static void nioTest(String src, String dest) {
long time = new Date().getTime();
try {
catFiles(src, dest);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long run = new Date().getTime() - time;
System.out.println("nio运行时长:" + run);
} public static void nio2Test(String src, String dest) {
long time = new Date().getTime();
try {
catFiles2(src, dest);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long run = new Date().getTime() - time;
System.out.println("nio分段传输 运行时长:" + run);
} /**
* nio channel-channel
*
* @param target
* @param src
* @throws Exception
*/
private static void catFiles(String src, String dest) throws Exception {
FileInputStream fis = new FileInputStream(src);
FileChannel channel = fis.getChannel();
FileOutputStream fos = new FileOutputStream(dest);
// channel-to-channel 快速传递数据 超过2G的传递会被截断 int.MaxValue
channel.transferTo(0, channel.size(), fos.getChannel());
channel.close();
fos.close();
fis.close();
}
/**
* 定义2G的buffer长度
*/
private static final long bufLength = 1024 * 1024 * 1024 * 2L;// 每次拷贝2GB的数据(上限)
/**
* nio channel-channel
*
* @param target
* @param src
* @throws Exception
*/
private static void catFiles2(String src, String dest) throws Exception {
FileInputStream fis = new FileInputStream(src);
FileChannel input = fis.getChannel();
// channel-to-channel 快速传递数据 超过2G的传递会被截断 int.MaxValue
FileOutputStream fos = new FileOutputStream(dest);
FileChannel output = fos.getChannel();
long size = input.size();
while (input.position() < size) {
long count = input.transferTo(input.position(), bufLength, output);
input.position(input.position() + count);
}
input.close();
output.close();
fos.close();
fis.close();
}
}

二、测试结果展示

  简要分析:从迁移日志看, 使用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,即系统内存页最小分配单位。假如你还在自己写这个过程,可以考虑直接使用该方法。下面是方法内部源码,仅做参考。

public static long copy(Path source, OutputStream out) throws IOException {
// ensure not null before opening file
Objects.requireNonNull(out); try (InputStream in = newInputStream(source)) {
return copy(in, out);
}
} // buffer size used for reading and writing
private static final int BUFFER_SIZE = 8192; /**
* Reads all bytes from an input stream and writes them to an output stream.
*/
private static long copy(InputStream source, OutputStream sink)
throws IOException
{
long nread = 0L;
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = source.read(buf)) > 0) {
sink.write(buf, 0, n);
nread += n;
}
return nread;
}

 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. tone() 和 IRremote 冲突的解决办法

    tone()函数冲突 http://www.geek-workshop.com/thread-4037-1-1.html 可以自制函数newtone() void newtone(byte toneP ...

  2. Node.js之单利模式

    在iOS中我们经常用到单利模式,这样就能够实现在多处共享同一数据,那么在Node.js中也存在这种模式. 我们知道,Node.js中模块的概念很重要,在写模块的接口的时候,只需要暴露出一个实例对象就能 ...

  3. 浅谈Java Virtual Machine

          Java Virtual Machine 就是指Java虚拟器,以下简称VM.关于VM的概念,最早出自CPU模拟器,众所周知的PC上的游戏机模拟器采用的便是和Java VM类似的技术.ja ...

  4. linkin大话数据结构--Map

    Map 映射关系,也有人称为字典,Map集合里存在两组值,一组是key,一组是value.Map里的key不允许重复.通过key总能找到唯一的value与之对应.Map里的key集存储方式和对应的Se ...

  5. 计算机改名引发的ORA

    近期上班时,由于开机时老是提示" 局域网出现计算机重名冲突",于是把计算机名字给改了,从PC2010081312zeo改为了CXBIKKKKKKK,结果第二天来的时候,用PL/SQ ...

  6. tp5命令行基础

    命令行工具需要在命令行下面执行,请先确保你的php.exe已经加入了系统环境变量Path. 要执行命令,首先进入命令行,并切换当前目录到应用的根目录(也就是think文件所在目录)下面,执行: php ...

  7. awk批量处理文件夹中所有文件

       #c=``     done

  8. Win10下通过IIS调试ASP程序遇到的问题和解决方案

    最近维护了以前别人的写的一个ASP的系统,记录一下调试过程中的问题和解决方案. 环境篇 万维网发布服务(W3SVC)已经停止 问题: 万维网发布服务(W3SVC)已经停止.除非万维网发布服务(W3SV ...

  9. 【django之用户认证】

    一.auth模块 模块导入 from django.contrib import auth 主要方法如下: 1 .authenticate()    提供了用户认证,即验证用户名以及密码是否正确,一般 ...

  10. HTTP/HTTPS GET&POST两种方式的实现方法

    关于GET及POST方式的区别请参照前面文章:http://www.cnblogs.com/hunterCecil/p/5698604.html http://www.cnblogs.com/hunt ...