黑马程序员技术交流社区

标题: IO问题请教(文件复制效率的问题)请指教。 [打印本页]

作者: 杨鹏    时间: 2012-5-11 22:45
标题: IO问题请教(文件复制效率的问题)请指教。
import java.io.*;
//用不同的方法复制文件并统计执行时间
public class CopyFile_2 {
       
        public static void main(String[] args) throws IOException {
               
                long start = System.currentTimeMillis();
                copyFile_1();
                long end = System.currentTimeMillis();
                System.out.println(end-start+"毫秒");
               
                start = System.currentTimeMillis();
                copyFile_2();
                end = System.currentTimeMillis();
                System.out.println(end-start+"毫秒");
               
                start = System.currentTimeMillis();
                copyFile_3();
                end = System.currentTimeMillis();
                System.out.println(end-start+"毫秒");
               
                start = System.currentTimeMillis();
                copyFile_4();
                end = System.currentTimeMillis();
                System.out.println(end-start+"毫秒");
        }
       
        //基本读取方式
        public static void copyFile_1() throws IOException        {
                //创建IO流对象
                FileInputStream fis = new FileInputStream("D:\\Kugou\\in\\xuewei - jia.mp3");
                FileOutputStream fos = new FileOutputStream("jia1.mp3");
               
                int data = 0;
                //循环读写数据
                while ((data=fis.read())!=-1) {
                        fos.write(data);
                }
                        fis.close();
                        fos.close();               
        }
       
        //在基本读取的方式定义字节数组作为缓冲
        public static void copyFile_2() throws IOException        {
                //创建IO流对象
                FileInputStream fis = new FileInputStream("D:\\Kugou\\in\\xuewei - jia.mp3");
                FileOutputStream fos = new FileOutputStream("jia2.mp3");
               
                byte[] buf = new byte[1024];
                int len = 0;
                //循环读写数据
                while ((len=fis.read(buf))!=-1) {
                        fos.write(buf,0,len);
                }
                        fis.close();
                        fos.close();                       
        }
       
        //通过包装类来读取和写入文件
        public static void copyFile_3() throws IOException {
                //创建IO流对象
                BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:\\Kugou\\in\\xuewei - jia.mp3"));
                BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("jia3.mp3"));
               
                int data = 0;
                //循环读写数据
                while ((data=fis.read())!= -1) {
                        fos.write(data);
                }
                        fis.close();
                        fos.close();               
        }
       
        //在使用包装类也同时使用数组缓冲
        public static void copyFile_4() throws IOException {
                //创建IO流对象
                BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:\\Kugou\\in\\xuewei - jia.mp3"));
                BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("jia4.mp3"));
               
                int data = 0;
                byte[] buf = new byte[1024];
                //循环读写数据
                while ((data=fis.read(buf))!= -1) {
                        fos.write(buf,0,data);                                   
                }
                        fis.close();
                        fos.close();               
        }   
}
运行结果
59578毫秒
79毫秒
89毫秒
24毫秒

//为何第四种方式比较块呢,而且在包装类中已经封装得有数组,为何还需要在外面再定义一个数组呢?第一种方式为什么会比其他三种方式慢这么多?不解,请指教,先谢谢了。
作者: 高云飞    时间: 2012-5-11 23:14
我的看法是:当用read()时,每次读取一个字节的数据,这样1024个数据就要向系统发送读取请求1024次,系统再响应1024次。而如果先读入到1024的字节数组里,可以认为输入流只用读取了一次。
不过以此为代价,当一次读入的字节数组长度越大,复制出来的文件比原文件就越大。你可以定义一个很大的字节数组,例如我复制一个2m的歌曲,用4096为字节数组长度,比原文件大3k.
可以参看我的帖子:http://bbs.itheima.com/thread-13883-1-1.html
作者: 乔建国    时间: 2012-5-11 23:26
第四种方式 读入的时候是先将字节存储到一个定义好容量的数组中,然后再将该数组读入到内存缓存区,然后在读入到程序中,
          写出的时候,是先将字符数组,写出到缓存区,然后在写出到文件里。所以运行速度最快
第三种方式  读入的时候把一批字节逐个读入到内存缓存区中,写出的时候也是把一批字节逐个写出到内存缓存区,然后在写出到文件里的。
第二种方式  读入的时候是把字节先存放在一个固定容量的数组中,然后在读入程序,写出的时候也是一样的道理
第一种方式  读入的时候是把字节逐个读入到程序,写出的时候同理
作者: 云惟桉    时间: 2012-5-11 23:35
我分四种情况和楼主讨论一下吧:
1、直接定义文件流FileInput/FileOutputStream,这样的方式,是调用一次read方法,向操作系统请求一次分配,也就是说,频繁的去请求资源,效率很慢。
并且从流中也是一个字节一个字节读取的,然后一个字节一个字节写入,没写完就不会进行读,速度相当缓慢。
【另外:】操作系统中分为用户态和核心态,请求资源分配的时候就会切换到核心态,回到主线程的时候又切换回用户态。如此来回,时间开销是肯定不用说的了,很费时。
2、第二种用时和第三种相差不多。
第二种方式使用一次性读数组长度数据,然后再一次性写,可以连续读,然后连续写,效率就提高了。
实际上,理论来说是第三种效率稍高一些,因为使用了缓冲机制,一次性请求多个字节调入流中,然后再对缓冲区进行读取。
但是关键是第三种是读一次写一次,读要等待写,这样来回又增加了用时,所以关系大概如下:
缓冲机制+读一次写一次 <= 使用数组读写

但是具体情况并不一定都符合理论的,因为只有Main一个线程,所以,CPU在执行main的中途,可能还会切换去执行别的程序,说不定就是在使用BufferedReader的时候切换出去了。所以结果存在一点差异性,都可以理解的。只要知道这两种方法速度相近应该就可以了。

3、第四种情况拉开差异的原因,就是因为用了缓冲机制,加上数组,提高了两方面性能:
   a、使用缓冲机制,一次性请求多个字节,调入缓冲区,减少操作系统的用户/核心态切换次数,速度大大提高;
   b、使用数组,一次性从缓冲区读取一个数组长度的数据,然后一次性写,减少了读等待写的时间,速度再次提高。

综上就是产生楼主实验结果的原因。希望能帮到你。解释的不够专业也希望稍微谅解一下噢!
作者: 孙宇晨    时间: 2012-5-11 23:36
第一个方法连缓冲区都没使用. 相当于从源取一个写一个, 所以是非常慢的.
第四个方法比较快是应为他定义了一个数组.取出来放进数组里 再一次性写入目的 所以会快  
但是各有利弊.缺点是如果文件大小不明确 定义大了 浪费内存空间
作者: 杨鹏    时间: 2012-5-12 09:49
高云飞 发表于 2012-5-11 23:14
我的看法是:当用read()时,每次读取一个字节的数据,这样1024个数据就要向系统发送读取请求1024次,系统再 ...

谢谢,现在有点理解了。
作者: 杨鹏    时间: 2012-5-12 09:49
孙宇晨 发表于 2012-5-11 23:36
第一个方法连缓冲区都没使用. 相当于从源取一个写一个, 所以是非常慢的.
第四个方法比较快是应为他定义了一 ...

谢谢,让我又有新的认识了。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2