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/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年2月7日
下一篇 2020年2月7日

相关推荐

  • JDK8新增高效原子累加器LongAdder源码分析

    很久以前写过CAS应用之JUC下的原子类,但是LongAdder这个类没有去看,只是给了一个其他博客的参考链接。今天就自己来分析一下。 AtomicLong的问题和LongAdde…

    2020年5月19日
    0790
  • JDK8u20字符串去重

    优点:节省大量内存 缺点:略微多占用cpu时间,新生代回收时间略微增加 -XX:+UseStringDeduplication String s1 = new String("he…

    Java 2020年1月15日
    0770
  • PriorityQueue源码分析

    总结 总结放前面防止太长不看: PriorityQueue是个最小堆,如果要改变排序顺序只能重写比较器传入构造方法。 内部元素要么实现Comparable接口,要么传入比较器进行比…

    Java 2020年2月10日
    0270
  • ThreadPoolExecutor源码分析-线程池如何实现线程复用?

    线程的复用问题 在开始看线程池的源码之前,先来看这么一个问题: 一个Thread对象可以重复地调用start()方法吗? 试试就知道了: @Test public void tes…

    2020年5月21日
    02880
  • 阻塞队列BlockingQueue详解

    阻塞队列是生产者消费者模式的经典体现。 我们在曾在Java线程池详解中自己实现过一个阻塞队列,这篇文章我们来研究一下JDK中的阻塞队列: BlockingQueue接口主要方法 抛…

    Java 2020年2月14日
    0600
  • JavaIO-缓冲流与转换流

    缓冲流 概述 缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类: 字节缓冲流:BufferedInputStream,BufferedO…

    Java 2020年2月4日
    0130
  • ThreadLocal源码分析和相关理解

    总结 总结放前面防止太长不看: 每个线程都有一个threadLocals字段,是一个ThreadLocalMap的实例,所有的ThreadLocal代表的线程私有数据都存放在这里面…

    Java 2020年2月13日
    0210
  • JDK8-Stream流库详解

    流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。 流的创建 Collection.stream(…

    Java 2020年2月11日
    0150
  • Java自动装箱缓存机制

    尝试运行这段代码: 相似的两段代码,得到的结果却完全不相同。 首先要知道在java中==比较的是对象的引用,从直觉出发,无论是integer1、integer2还是integer3…

    Java 2019年12月5日
    0180
  • Java多线程共享模型之管程

    Monitor(锁) Monitor被翻译为监视器或管程。 Monitor是重量级锁的实现,是向操作系统申请的。 Monitor的结构:WaitSet、EntryList、Owne…

    2020年2月2日
    0930

发表回复

登录后才能评论