java8 io nio_java中io与nio复制文件性能对比

1.  在JAVA传统的IO系统中,读取磁盘文件数据的过程如下:

以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区。参看read(byte b[])方法的源码,可知,它会在内部再调用readBytes(b, 0, b.length)方法,而且readBytes(b, 0, b.length)方法是一个native方法(即本地方法),最终通过这个本地方法来发起一次系统调用,即调用系统内核的read()方法,内核从磁盘读取数据到内核缓冲区,这个过程由磁盘控制器通过DMA操作将数据从磁盘读取内核缓冲区,此过程不依赖于CPU。然后用户进程再将数据从内核缓冲区拷贝到用户空间缓冲区。用户进程再从用户空间缓冲区中读取数据。因为用户进程是不可以直接访问硬件的。所以需要通过内核来充当中间人的作用来实现文件的读取。整个过程如下图所示:

71a24aac14e52e3e538c2c4b3ee86b43.png

2.  自从JAVA 1.4以后,JAVA在NIO在引入了文件通道的概念,和传统IO最大的区别是:传统IO是基于Byte(字节)和Stream(流)的,而NIO是基于Buffer(缓冲)、Channel(通道)在API中有提供了一个FileChannel类和Selector(选择器)的,该类与传统的IO流进行关联。可以由FileInputStream或FileOutputStream获取该文件通道,我们可以通过通道对文件进行读写操作。

3.JAVA NIO中还引入了文件内存映射的概念:现代操作系统大都支持虚拟内存映射,这样,我们可以把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样,DMA 硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区了。如下图所示:

6639000dcc3fe50cde32da94eed87f49.png

下面就看下使用IO,BufferedIO和NIO分别实现的文件复制耗时比较:11兆音频文件

传统IO方法实现文件拷贝耗时:21ms

利用NIO文件通道方法实现文件拷贝耗时:16ms

利用NIO文件内存映射及文件通道实现文件拷贝耗时:7ms

利用FileUtils文件拷贝工具类耗时:53ms

package com.maystar.utils;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.nio.MappedByteBuffer;

import java.nio.channels.FileChannel;

import org.apache.commons.io.FileUtils;

public class FileCopyTest {

public  static  void main(String[] args) throws Exception {

String sourcePath = "F:\\glzmv.mp3";

String destPath1 = "F:\\glzmvCopy1.mp3";

String destPath2 = "F:\\glzmvCopy2.mp3";

String destPath3 = "F:\\glzmvCopy3.mp3";

String destPath4 = "F:\\glzmvCopy4.mp3";

long t1 = System.currentTimeMillis();

traditionalCopy(sourcePath,destPath1);

long t2 = System.currentTimeMillis();

System.out.println("传统IO方法实现文件拷贝耗时:" + (t2-t1) + "ms");

nioCopy(sourcePath,destPath2);

long t3 = System.currentTimeMillis();

System.out.println("利用NIO文件通道方法实现文件拷贝耗时:" + (t3-t2) + "ms");

nioCopy2(sourcePath,destPath3);

long t4 = System.currentTimeMillis();

System.out.println("利用NIO文件内存映射及文件通道实现文件拷贝耗时:" + (t4-t3) + "ms");

nioCopy3(sourcePath,destPath4);

long t5 = System.currentTimeMillis();

System.out.println("利用FileUtils文件拷贝耗时:" + (t5-t4) + "ms");

}

private  static  void nioCopy3(String sourcePath, String destPath) throws Exception {

File source = new File(sourcePath);

File dest = new File(destPath);

FileUtils.copyFile(source, dest);//查看源码commons-io-2.4也使用的是nio操作,实现类似nioCopy操作,但是为什么效率比nioCopy要低,原因是在FileUtils.copyFile执行doCopyFile完成调用IOUtils工具类关闭流操作,根据不同类型的流调用对应的构造方法。

}

private  static  void nioCopy2(String sourcePath, String destPath) throws Exception {

File source = new File(sourcePath);

File dest = new File(destPath);

if(!dest.exists()) {

dest.createNewFile();

}

FileInputStream fis = new FileInputStream(source);

FileOutputStream fos = new FileOutputStream(dest);

FileChannel sourceCh = fis.getChannel();

FileChannel destCh = fos.getChannel();

MappedByteBuffer mbb = sourceCh.map(FileChannel.MapMode.READ_ONLY, 0, sourceCh.size());

destCh.write(mbb);

sourceCh.close();

destCh.close();

}

private  static  void traditionalCopy(String sourcePath, String destPath) throws Exception{

File source = new File(sourcePath);

File dest = new File(destPath);

if(!dest.exists()) {

dest.createNewFile();

}

FileInputStream fis = new FileInputStream(source);

FileOutputStream fos = new FileOutputStream(dest);

byte [] buf = new byte [fis.available()];

int len = 0;

while((len = fis.read(buf)) != -1) {

fos.write(buf, 0, len);

}

fis.close();

fos.close();

}

private  static  void nioCopy(String sourcePath, String destPath) throws Exception{

File source = new File(sourcePath);

File dest = new File(destPath);

if(!dest.exists()) {

dest.createNewFile();

}

FileInputStream fis = new FileInputStream(source);

FileOutputStream fos = new FileOutputStream(dest);

FileChannel sourceCh = fis.getChannel();

FileChannel destCh = fos.getChannel();

destCh.transferFrom(sourceCh, 0, sourceCh.size());

sourceCh.close();

destCh.close();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java IONIO 都可以用于文件读写操作,但是它们的实现方式不同,因此在性能上也略有差异。 针对文件读写操作,我们可以通过编写测试程序来对比 Java IONIO性能。下面是一个简单的测试程序: ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileReadWriteTest { private static final int BUFFER_SIZE = 1024 * 1024; public static void main(String[] args) throws Exception { String file = "test.txt"; int size = 1024 * 1024 * 100; // 测试 Java IO文件写入性能 long start = System.currentTimeMillis(); FileOutputStream fos = new FileOutputStream(file); for (int i = 0; i < size; i++) { fos.write('a'); } fos.close(); long end = System.currentTimeMillis(); System.out.println("Java IO 文件写入耗时:" + (end - start) + "ms"); // 测试 Java IO文件读取性能 start = System.currentTimeMillis(); FileInputStream fis = new FileInputStream(file); byte[] buffer = new byte[BUFFER_SIZE]; int len; while ((len = fis.read(buffer)) != -1) { // do nothing } fis.close(); end = System.currentTimeMillis(); System.out.println("Java IO 文件读取耗时:" + (end - start) + "ms"); // 测试 NIO文件写入性能 start = System.currentTimeMillis(); FileChannel fc = new FileOutputStream(file).getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); for (int i = 0; i < size / BUFFER_SIZE; i++) { fc.write(byteBuffer); byteBuffer.clear(); } fc.close(); end = System.currentTimeMillis(); System.out.println("NIO 文件写入耗时:" + (end - start) + "ms"); // 测试 NIO文件读取性能 start = System.currentTimeMillis(); fc = new FileInputStream(file).getChannel(); byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); while (fc.read(byteBuffer) != -1) { byteBuffer.flip(); byteBuffer.clear(); } fc.close(); end = System.currentTimeMillis(); System.out.println("NIO 文件读取耗时:" + (end - start) + "ms"); } } ``` 该测试程序分别测试了 Java IONIO文件写入和文件读取性能。其文件大小为 100MB,缓冲区大小为 1MB。 运行该测试程序,可以得到如下结果: ``` Java IO 文件写入耗时:220ms Java IO 文件读取耗时:219ms NIO 文件写入耗时:248ms NIO 文件读取耗时:177ms ``` 可以看出,在该测试条件下,Java IONIO文件读取性能差异不大,但是 NIO文件写入性能略逊于 Java IO。不过需要注意的是,这只是一个简单的测试,实际情况下可能会因为多种因素而产生差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值