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. socket 编程--sockaddr与sockaddr_in区别与联系(转)

    在linux环境下,结构体struct sockaddr在/usr/include/linux/socket.h中定义,具体如下:typedef unsigned short sa_family_t; ...

  2. Linuxc - 操作系统内存分配

    静态变量是存储在数据段的,在函数中可以共用. 全局变量也是存储在数据段的,在全局中可以共用. 指针变量本质上是地址,数组变量本质上也是地址. 数组是可靠的,不可变的地址.指针变量是不可靠的,可变的.数 ...

  3. shopnc前台登陆不进去解决方法

    安装好shopnc后,注册新用户成功,且登陆后提示登陆成功,但是一两秒后自动跳转回登陆页面,需要重新登陆问题 PHP写session不是自动起的,需要修改后才行,所以 找到PHP的php.ini配置文 ...

  4. 理解Python中的装饰器//这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档

    转自:http://www.cnblogs.com/rollenholt/archive/2012/05/02/2479833.html 这篇文章将python的装饰器来龙去脉说的很清楚,故转过来存档 ...

  5. ajax请求返回乱码

    1,web.xml中有如下配置: <!-- 编码过滤器 --> <filter> <filter-name>encodingFilter</filter-na ...

  6. Linux修改主机名脚本-不重启-支持RedHat、SUSE

    需要用脚本修改主机名,涉及RedHat.SUSE系统,并且要求修改立即生效且不重启,下面就是我的脚本. 使用脚本的方法如下: 1 首先创建一个脚本文件,假如命名为ModifyHostname.sh: ...

  7. awk解决实际问题例子

    awk很有用,就不说了,下面记录几个我遇到的例子,汇总 1 获得linux环境变量(ENVIRON) awk 'BEGIN{print ENVIRON["PATH"];}' /et ...

  8. js实现最短时间走完不同速度的路程

    题目: 现在有一条公路,起点是0公里,终点是100公里.这条公路被划分为N段,每一段有不同的限速.现在他们从A公里处开始,到B公里处结束.请帮他们计算在不超过限速的情况下,最少需要多少时间完成这段路程 ...

  9. 使用CefSharp开发一个12306“安心刷票弹窗通知”工具

    有需求就要改进 最近两年没有在春节回家过年了,主要是票太难买,虽然之前写了一个12306“无声购票弹窗”工具,解决了抢票问题,但是全家老小一起回去还是很累,干脆就在北京过年了.这两天突然有一个朋友问我 ...

  10. WEB渗透测试之漏扫神器

    AppScan 对现代 Web 应用程序和服务执行自动化的动态应用程序安全测试(DAST) 和交互式应用程序安全测试 (IAST).支持 Web 2.0. JavaScript 和 AJAX 框架的全 ...