• NIO


    从jdk1.4开始,java就提供了NIO,所以再用io是多么不知耻啊。

    Channel和Buffer是NIO中的两个核心对象。

    Channel是通道的意思,NIO中所有数据都需要通过Channel传输,

    Buffer可以理解为一个容器,它的本质是一个数组,发送到Channel中的数据以及从Channel中读取的数据,都必须先放到Buffer中。

    Buffer是个抽象类,常用子类有ByteBuffer,此外,除boolean之外的六个原始类型都有对应的Buffer类,如ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer、CharBuffer。这些Buffer子类内部都有一个相应类型的数组,如ByteBuffer内部有一个byte数组,CharBuffer内部有一个char数组。Buffer子类都没有提供public的构造方法,只可以通过allocate开头的静态方法或者wrap静态方法生成Buffer实例。

    public static ByteBuffer allocate(int capacity):返回一个HeapByteBuffer实例,capacity值为capacity,limit值也为capacity,position值为0。

    ByteBuffer还有一个MappedByteBuffer子类,表示Channel将文件的部分或全部内容映射到内存中后得到的结果,通常由Channel的map()方法返回。

    在Buffer中有三个重要的概念:容量(capacity)、界限(limit)、位置(position)。

    capacity:Buffer的容量表示该Buffer最多能存储多少数据。容量在Buffer实例创建后不可改变。可以通过Buffer的capacity()实例方法查看capacity。

    limit:位于界限之后的数据既不可读,也不可写。可以通过Buffer的limit()实例方法查看limit。

    position:记录指针。可以通过Buffer的position()实例方法查看position。

    Buffer的主要作用就是装入数据,然后输出数据。Buffer有两个重要的实例方法,flip()和clear()。

    flip方法实现是:

        public final Buffer flip() {
            limit = position;
            position = 0;
            mark = -1;
            return this;
        }

    flip方法会将limit值置为position,将position值置为0,将mark置为-1。

    clear方法实现是:

        public final Buffer clear() {
            position = 0;
            limit = capacity;
            mark = -1;
            return this;
        }

    clear方法会将position值置为0,limit值置为capacity,mark置为-1。这三个参数的状态和调用allocate(capacity)生成一个Buffer实例时的状态一样,所以说clear方法是为向Buffer中放入数据做准备。

    往Buffer中放入数据时,limit值如何变化呢???答案是不变。从Buffer中取数据时,limit值如何变化呢???答案还是不变。

    Channel

    Channel是个接口,常用实现类有FileChannel、SelectableChannel、ServerSocketChannel、SocketChannel、DatagramChannel。FileChannel是操作文件的Channel实现类,ServerSocketChannel、SocketChannel是用于支持TCP通信的Channel实现类,DatagramChannel是用于支持UDP通信的Channel实现类。

    如果要从Channel中取数据,必须先用Buffer从Channel中取数据,然后再从Buffer中取数据。反之亦然,如果想往Channel中写数据,则必须先将数据写入Buffer中,再将Buffer中的数据写入Channel中。

    示例:

        public static void main(String[] args) {
            File srcFile = new File("d:/CentOS-7-3.iso");
            File destFile = new File("d:/CentOS-7-20.iso");
            try (FileInputStream inputStream = new FileInputStream(srcFile);
                 FileChannel inChannel = inputStream.getChannel();
                 FileOutputStream outputStream = new FileOutputStream(destFile);
                 FileChannel outChannel = outputStream.getChannel()) {
                long size = srcFile.length();
                // 一次映射1G到内存中
                long pageSize = 1020 * 1024 * 1024;
                long time = size / pageSize;
                time = time * pageSize == size ? time : time + 1;
                MappedByteBuffer mappedByteBuffer;
                for (int i = 0; i < time; i++) {
                    long currentPageSize = i < time - 1 ? pageSize : size - (time - 1) * pageSize;
                    mappedByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, i * pageSize, currentPageSize);
                    outChannel.write(mappedByteBuffer, i * pageSize);
                    mappedByteBuffer.clear();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    InputStream实例生成的Channel实例调用map方法时,MapMode只能是MapMode.READ_ONLY,否则会抛NonWritableChannelException异常。

    FileUtils工具类的copyFile方法也用于文件复制,查看其源码,核心是:

         try (FileInputStream fis = new FileInputStream(srcFile);
                 FileChannel input = fis.getChannel();
                 FileOutputStream fos = new FileOutputStream(destFile);
                 FileChannel output = fos.getChannel()) {
                final long size = input.size(); 
                long pos = 0;
                long count = 0;
                while (pos < size) {
                    final long remain = size - pos;
                    count = remain > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : remain;
                    final long bytesCopied = output.transferFrom(input, pos, count);
                    if (bytesCopied == 0) { 
                        break; 
                    }
                    pos += bytesCopied;
                }
            }

    可以看出FileUtils的copyFile方法用的是Channel的transferFrom方法,方法注释上说,这个方法不用经过Buffer,直接在input channel和output channel之间transfer数据,比output channel write (input channel map的Buffer)高效,FileChannel还有一个类似的transferTo方法可以使用。

    通信

    NIO为非阻塞式Socket通信提供了几个特殊的类:

    1、Selector:它是SelectableChannel的多路复用器,所有SelectableChannel实例都要注册到Selector实例上。调用Selector的open()静态方法可以生成一个Selector实例。

    2、SelectableChannel:可以被多路复用的Channel,被注册到Selector上,常用子类有:描述tcp通信的ServerSocketChannel、SocketChannel,以及描述udp通信的DatagramChannel。

    3、SelectionKey:表示SelectableChannel与Selector的注册关系。

    调用SelectableChannel实例的register()方法可将其注册到Selector实例上,当该Selector实例上的某些SelectableChannel有需要处理的IO操作时,可调用Selector实例的select()方法获取这些SelectableChannel实例的数量,并可通过selectedKeys()方法返回对应的SelectionKey集合,通过该集合就可以获取所有需要进行IO处理的SelectableChannel了。

  • 相关阅读:
    详细解说仿制QQ列表 展开和收起列表
    带大家一步一步封装聊天键盘(三)新增功能不要错过哟
    带大家一步一步封装一个聊天键盘(二)
    带大家一步一步的封装一个聊天键盘(一)
    iOS中的屏幕适配之Autolayout(初级)
    iOS开发中tableViewCell的悬浮效果
    [Leetcode] 1343. Maximum Product of Splitted Binary Tree | 分裂二叉树的最大乘积
    [Leetcode] 560. Subarray Sum Equals K | 和为K的子数组
    爆炸!iOS资源大礼包(持续更新...)
    Objective-C探究alloc方法的实现
  • 原文地址:https://www.cnblogs.com/koushr/p/5873386.html
Copyright © 2020-2023  润新知