ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

(五)非阻塞式编程NIO

2021-03-03 17:05:01  阅读:237  来源: 互联网

标签:NIO buffer 编程 阻塞 source File new fout fin


1.NIO概述

Channel与Stream的不同:

  • Stream是具有方向性的,有输入流 or 输出流;Channel是双向的,既可以读又可以写。
  • Stream的读和写都是阻塞式的;但是Channel有两种模式,既可以阻塞式读写,又可以非阻塞式读写。

Note:如果一个任务单线程就可以执行,那么往往比使用多线程效率要高,NIO就是一个例子。多线程不一定更有效率,因为:

  • 如果需要处理线程的数量多于CPU处理器的数量,会出现“上下文交换”。CPU的每一次切换都需要先保存当前线程的状态,之后重新执行该线程时,要加载原先线程的状态。在各个线程发生的交换过程,需要消耗系统资源;
  • 每创建一个线程,系统都要为其分配一定的系统资源。

2.Buffer简析

 

3.Channel简析

 

4.使用BIO和NIO实战:本地文件拷贝

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

public class FileCopyDemo {
    private static final int ROUNDS = 3; // 每种方法都运行3次来评估性能

    // 执行不同方法的函数,并评估性能
    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;
            if (i != ROUNDS - 1) {
                target.delete(); // 每次拷贝后再删除
            }
        }
        System.out.println(test + ":" + elapsed / ROUNDS);
    }

    // 关闭各种实现了Closeable接口的资源
    private static void colse(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {

        // 一、不使用缓冲的流的拷贝(一个字节一个字节地拷贝)
        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;
                    try {
                        // 如果读到数据的结尾时,会返回-1
                        while ((result = fin.read()) != -1) {
                            // 每读到一个字节,就把字节写到文件输出流中
                            fout.write(result);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } finally {
                    colse(fin);
                    colse(fout);
                }
            }

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

        // 二、使用缓冲区的流的拷贝
        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));

                    // 缓冲区大小可以自己定义,例如定义为1024字节.
                    byte[] buffer = new byte[1024];

                    int result;
                    while ((result = fin.read(buffer)) != -1) {
                        // 一次可读1024个字节,而不是1个字节了. result返回当前从buffer中读取的字节数
                        fout.write(buffer,0,result);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    colse(fin);
                    colse(fout);
                }
            }

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

        // 三、channel与buffer做数据交换 nio
        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从写模式切换到读模式,内部通过调整position指针和limit指针来实现
                        buffer.flip();
                        // 加循环的作用:为了保证把buffer中所有的数据都读取到目标文件通道中
                        while (buffer.hasRemaining()) {
                            fout.write(buffer);
                        }
                        // 读模式调整为写模式,内部通过使position指针回到初始位置,limit回到最远端
                        buffer.clear();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    colse(fin);
                    colse(fout);
                }
            }

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

        // 四、通道与通道间传输数据 nio
        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 transfered = 0L; // 目前为止已经拷贝了多少字节的数据
                    long size = fin.size();  // 要复制文件的大小
                    // transferTo()函数不能保证把原通道所有数据都传输到目标通道
                    while (transfered != size) {
                        transfered += fin.transferTo(0, size, fout); // 第二个参数是要传输多少数据
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    colse(fin);
                    colse(fout);
                }
            }

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


        File source = new File("F:\\test\\project.zip");
        File target1 = new File("F:\\test\\p1.zip");
        File target2 = new File("F:\\test\\p2.zip");
        File target3 = new File("F:\\test\\p3.zip");
        File target4 = new File("F:\\test\\p4.zip");

        benchmark(noBufferStreamCopy, source, target1);
        benchmark(bufferedStreamCopy, source, target2);
        benchmark(nioBufferCopy, source, target3);
        benchmark(nioTransferCopy, source, target4);
    }
}

 

5.实验结果

  • 以复制大小为2.75MB的文件为例,一个字节一个字节地拷贝实在是慢的可怕。。

6.Selector简析

  • 作用:不停地“询问”通道什么时候处于可操作状态,即监听多个通道的状态。

 

  •  select()返回的数值表示目前有几个Channel处于可操作状态。
  • 目的是,跟Selector这一个对象互动,就可以得到Selector所监听的多个Channel对象状态的改变,由此实现进一步的业务逻辑。

 

标签:NIO,buffer,编程,阻塞,source,File,new,fout,fin
来源: https://www.cnblogs.com/HuangYJ/p/14454094.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有