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. 百度Apollo 尝试

    从Git-Hub上下载了Apollo源码在Ubuntu上准备运行一下 完成了以下步骤: bash docker/scripts/install_docker.sh bash docker/script ...

  2. Intel系列微处理器的三种工作模式

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  3. Spring注解装配

    Spring 自动装配的主机有 @Autowired.@Intect.@Resource @Autowired是byType的, @Resource是byName的.我们一般用@Atutowired. ...

  4. tone() 和 IRremote 冲突的解决办法

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

  5. python_tornado_session用户验证

    什么是session? -- Django中带有session,tornado中自己写 -- 逻辑整理 用户请求过来,验证通过,随机生成一个字符串当作value返回给浏览器, 在服务器中用户信息与随机 ...

  6. sqlserver2008客户端设置主键自增

    是标识改为是

  7. elasticsearch java和_head插件对索引文档的增删改查

    利用head插件: 1,创建索引并添加一条数据(yananindex:索引名称,yanantype:索引类型,1:索引id) 2.修改索引数据(索引id1不变,_version是对该索引数据执行了几次 ...

  8. DirectX SDK (June 2010)安装错误S1023的一个解决方法

    在安装DXSDK_Jun10.exe时一个常见的安装失败的代号是S1023,一般出现这种错误的原因是系统中已经安装了Visual Studio 2010及以上的版本. 在[控制面板]中找到这两个: 如 ...

  9. 101490E Charles in Charge

    题目连接 http://codeforces.com/gym/101490 题目大意 你有一张图,每两点之间有一定距离,计算出比最短路大x%之内的路径中最长边的最小值 分析 先跑一遍最短路,然后二分答 ...

  10. vs调试dll工程

    dll本身是没法运行的,必须在其它工程调用dll时候才会运行. 所以,调试dll首先要将调用dll的工程和dll工程联系起来. 解决方案中添加dll工程: 现在dll 和 应用程序两个工程就都在一个解 ...