NIO、BIO模型对比实现文件的复制

NIO特点

  • 使用Channel代替Stream,是双向的
  • 使用Selector监控多条Channel
  • 可以在一个线程里处理多个Channel I/O

Channel和Buffer

Buffer

应用程序对Channel的读写必须通过Buffer。Buffer实质上是一个数组,通常是字节数组,JAVA中的八种基本类型都有各自对应的Buffer。

Buffer包含三个指针:

  • capacity:最大容量
  • limit:限制位置
  • position:要操作的位置

Buffer既可以读也可以写,但读和写之间必须进行模式切换,常用API:

flip(): 翻转模式:写模式->读模式

把position还原回初始位置,limit指针指向刚才position指向的位置。position和limit之间即为要读的数据。

clear(): 翻转模式:写模式->读模式
n
position还原初始位置,limit指向capacity。

compact(): 翻转模式:写模式->读模式

如果只读取了一部分,剩下的一部分以后再读:把未读取的数据拷贝到最开始的位置,position指向未读数据后面的位置,limit指向capacity

Channel

通道之间进行数据的传输,类似于Stream,可不经过Buffer之间transfer数据

主要有如下几种Channel

  • FileChannel
  • ServerSocketChannel
  • SocketChannel

NIO和BIO实现文件的复制

声明接口:

interface FileCopyRunner{
    void copyFile(File source, File target);
}

关闭资源方法:

private static void close(Closeable... closeable){
    for(Closeable c:closeable){
        try {
            c.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BIO,不使用缓冲流:

FileCopyRunner noBufferStreamCopy = new FileCopyRunner() {
    @Override
    public void copyFile(File source, File target) {
        InputStream fin = null;
        OutputStream fout = null;
        try {
            fin = new FileInputStream(source);
            fout = new FileOutputStream(target);
            int result;
            while((result = fin.read())!=-1){
                fout.write(result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(fin,fout);
        }
    }

    @Override
    public String toString() {
        return "noBufferStreamCopy";
    }
};

BIO,使用缓冲流:

FileCopyRunner bufferedStreamCopy = new FileCopyRunner() {
    @Override
    public void copyFile(File source, File target) {
        InputStream fin = null;
        OutputStream fout = null;
        try {
            fin = new BufferedInputStream(new FileInputStream(source));
            fout = new BufferedOutputStream(new FileOutputStream(target));

            byte[] buffer = new byte[1024];
            int result;
            while((result = fin.read(buffer))!=-1){
                fout.write(buffer,0,result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(fin,fout);
        }
    }
    @Override
    public String toString() {
        return "bufferedStreamCopy";
    }
};

NIO,经过Buffer:

FileCopyRunner nioBufferCopy = new FileCopyRunner() {
    @Override
    public void copyFile(File source, File target) {
        FileChannel fin = null;
        FileChannel fout = null;
        try {
            fin = new FileInputStream(source).getChannel();
            fout = new FileOutputStream(target).getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (fin.read(buffer)!=-1){
                buffer.flip();
                while (buffer.hasRemaining()){
                    fout.write(buffer);
                }
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(fin,fout);
        }
    }
    @Override
    public String toString() {
        return "nioBufferCopy";
    }
};

NIO,不经过Buffer:

FileCopyRunner nioTransferCopy = new FileCopyRunner() {
    @Override
    public void copyFile(File source, File target) {
        FileChannel fin = null;
        FileChannel fout = null;
        try {
            fin = new FileInputStream(source).getChannel();
            fout = new FileOutputStream(target).getChannel();
            long transferred = 0L;
            long size = fin.size();
            while (transferred!=size) {
                transferred += fin.transferTo(0,size,fout);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(fin,fout);
        }
    }
    @Override
    public String toString() {
        return "nioTransferCopy";
    }
};

可以编写函数进行性能测试:

private static final int ROUNDS = 5;

private static void benchmark(FileCopyRunner test,File source,File target){
    long elapsed = 0L;
    for(int i = 0;i<ROUNDS;i++){
        long startTime = System.currentTimeMillis();
        test.copyFile(source,target);
        elapsed += System.currentTimeMillis() - startTime;
        target.delete();
    }
    System.out.println(test+":"+elapsed/ROUNDS);
}

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/nio%e3%80%81bio%e6%a8%a1%e5%9e%8b%e5%af%b9%e6%af%94%e5%ae%9e%e7%8e%b0%e6%96%87%e4%bb%b6%e7%9a%84%e5%a4%8d%e5%88%b6/

发表评论

电子邮件地址不会被公开。