zoukankan      html  css  js  c++  java
  • 我要学java-nio-1

    解释:Java NIO是java 1.4之后新出的一套IO接口,这里的的新是相对于原有标准的Java IO和Java Networking接口。NIO提供了一种完全不同的操作方式。

    Java NIO: Channels and Buffers:标准的IO编程接口是面向字节流和字符流的。而NIO是面向通道和缓冲区的,数据总是从通道中读到buffer缓冲区内,或者从buffer写入到通道中。

    Java NIO: Non-blocking IO:Java NIO使我们可以进行非阻塞IO操作。比如说,单线程中从通道读取数据到buffer,同时可以继续做别的事情,当数据读取到buffer中后,线程再继续处理数据。写数据也是一样的。

    Java NIO: Selectors:NIO中有一个“slectors”的概念。selector可以检测多个通道的事件状态(例如:链接打开,数据到达)这样单线程就可以操作多个通道的数据。 所有这些都会在后续章节中更详细的介绍。

    Channel的基础示例(Basic Channel Example)

    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    
    public class Demo1 {
        public static void main(String[] args) throws Exception {
            RandomAccessFile aFile = new RandomAccessFile("d:/tmp/1.txt", "rw");
            FileChannel inChannel = aFile.getChannel();
            ByteBuffer buf = ByteBuffer.allocate(48);
            int bytesRead = inChannel.read(buf);
            while (bytesRead != -1) {
                System.out.println("Read " + bytesRead);
                buf.flip();
                while(buf.hasRemaining()){
                    System.out.print((char) buf.get());
                }
                buf.clear();
                bytesRead = inChannel.read(buf);
            }
            aFile.close();
        }
    }

    通过RandomAccessFile得到 FileChannel  ,buf.flip()的调用。首先把数据读取到Buffer中,然后调用flip()方法。接着再把数据读取出来。

    Buffer基本用法(Basic Buffer Usage)
    利用Buffer读写数据,通常遵循四个步骤:

    1,把数据写入buffer;
    2,调用flip;
    3,从Buffer中读取数据;
    4,调用buffer.clear()或者buffer.compact()

    当写入数据到buffer中时,buffer会记录已经写入的数据大小。当需要读数据时,通过flip()方法把buffer从写模式调整为读模式;在读模式下,可以读取所有已经写入的数据。当读取完数据后,需要清空buffer,以满足后续写入操作。清空buffer有两种方式:调用clear()或compact()方法。clear会清空整个buffer,compact则只清空已读取的数据,未被读取的数据会被移动到buffer的开始位置,写入位置则近跟着未读数据之后。

    Buffer的容量,位置,上限(Buffer Capacity, Position and Limit)
    buffer缓冲区实质上就是一块内存,用于写入数据,也供后续再次读取数据。这块内存被NIO Buffer管理,并提供一系列的方法用于更简单的操作这块内存。
    一个Buffer有三个属性是必须掌握的,分别是:
    1,capacity容量
    2,position位置
    3,limit限制
    position和limit的具体含义取决于当前buffer的模式。

    容量(Capacity)
    作为一块内存,buffer有一个固定的大小,叫做capacity容量。也就是最多只能写入容量值得字节,整形等数据。一旦buffer写满了就需要清空已读数据以便下次继续写入新的数据。
    位置(Position)
    当写入数据到Buffer的时候需要中一个确定的位置开始,默认初始化时这个位置position为0,一旦写入了数据比如一个字节,整形数据,那么position的值就会指向数据之后的一个单元,position最大可以到capacity-1.
    当从Buffer读取数据时,也需要从一个确定的位置开始。buffer从写入模式变为读取模式时,position会归零,每次读取后,position向后移动。
    上限(Limit)
    在写模式,limit的含义是我们所能写入的最大数据量。它等同于buffer的容量。
    一旦切换到读模式,limit则代表我们所能读取的最大数据量,他的值等同于写模式下position的位置。

    数据读取的上限时buffer中已有的数据,也就是limit的位置(原position所指的位置)。

    Buffer Types
    Java NIO有如下具体的Buffer类型:
    ByteBuffer
    MappedByteBuffer
    CharBuffer
    DoubleBuffer
    FloatBuffer
    IntBuffer
    LongBuffer
    ShortBuffer
    Buffer的类型代表了不同数据类型,换句话说,Buffer中的数据可以是上述的基本类型;
    MappedByteBuffer稍有不同,我们会单独介绍。
    分配一个Buffer(Allocating a Buffer)
    为了获取一个Buffer对象,你必须先分配。每个Buffer实现类都有一个allocate()方法用于分配内存。下面看一个实例,开辟一个48字节大小的buffer:
    ByteBuffer buf = ByteBuffer.allocate(48);
    开辟一个1024个字符的CharBuffer:
    CharBuffer buf = CharBuffer.allocate(1024);
    写入数据到Buffer(Writing Data to a Buffer)
    写数据到Buffer有两种方法:
    从Channel中写数据到Buffer
    手动写数据到Buffer,调用put方法
    下面是一个实例,演示从Channel写数据到Buffer:
     int bytesRead = inChannel.read(buf); //read into buffer.
    通过put写数据:
    buf.put(127);    
    put方法有很多不同版本,对应不同的写数据方法。例如把数据写到特定的位置,或者把一个字节数据写入buffer。看考JavaDoc文档可以查阅的更多数据。
    翻转(flip())
    flip()方法可以吧Buffer从写模式切换到读模式。调用flip方法会把position归零,并设置limit为之前的position的值。 也就是说,现在position代表的是读取位置,limit标示的是已写入的数据位置。
    从Buffer读取数据(Reading Data from a Buffer)
    冲Buffer读数据也有两种方式。
    
    从buffer读数据到channel
    从buffer直接读取数据,调用get方法
    读取数据到channel的例子:
    //read from buffer into channel.
    int bytesWritten = inChannel.write(buf);
    调用get读取数据的例子:
    byte aByte = buf.get();    
    get也有诸多版本,对应了不同的读取方式。
    rewind()
    Buffer.rewind()方法将position置为0,这样我们可以重复读取buffer中的数据。limit保持不变。
    clear() and compact()
    一旦我们从buffer中读取完数据,需要复用buffer为下次写数据做准备。只需要调用clear或compact方法。
    clear方法会重置position为0,limit为capacity,也就是整个Buffer清空。实际上Buffer中数据并没有清空,我们只是把标记为修改了。
    如果Buffer还有一些数据没有读取完,调用clear就会导致这部分数据被“遗忘”,因为我们没有标记这部分数据未读。
    针对这种情况,如果需要保留未读数据,那么可以使用compact。 因此compact和clear的区别就在于对未读数据的处理,是保留这部分数据还是一起清空。
    mark() and reset()
    通过mark方法可以标记当前的position,通过reset来恢复mark的位置,这个非常像canva的save和restore:
    buffer.mark();
    //call buffer.get() a couple of times, e.g. during parsing.
    buffer.reset();  //set position back to mark.    
    equals() and compareTo()
    可以用eqauls和compareTo比较两个buffer
    equals()
    判断两个buffer相对,需满足:
    类型相同
    buffer中剩余字节数相同
    所有剩余字节相等
    从上面的三个条件可以看出,equals只比较buffer中的部分内容,并不会去比较每一个元素。
    compareTo()
    compareTo也是比较buffer中的剩余元素,只不过这个方法适用于比较排序的

    Java NIO发布时内置了对scatter / gather的支持。scatter / gather是通过通道读写数据的两个概念。

    Scattering read指的是从通道读取的操作能把数据写入多个buffer,也就是sctters代表了数据从一个channel到多个buffer的过程。

    gathering write则正好相反,表示的是从多个buffer把数据写入到一个channel中。

    Scatter/gather在有些场景下会非常有用,比如需要处理多份分开传输的数据。举例来说,假设一个消息包含了header和body,我们可能会把header和body保存在不同独立buffer中,这种分开处理header与body的做法会使开发更简明。

    Scattering Reads

    "scattering read"是把数据从单个Channel写入到多个buffer

    Java NIO: Scattering Read

    用代码来表示的话如下:

    ByteBuffer header = ByteBuffer.allocate(128);
    ByteBuffer body   = ByteBuffer.allocate(1024);
    ByteBuffer[] bufferArray = { header, body };
    channel.read(bufferArray);

    观察代码可以发现,我们把多个buffer写在了一个数组中,然后把数组传递给channel.read()方法。read()方法内部会负责把数据按顺序写进传入的buffer数组内。一个buffer写满后,接着写到下一个buffer中。

    实际上,scattering read内部必须写满一个buffer后才会向后移动到下一个buffer,因此这并不适合消息大小会动态改变的部分,也就是说,如果你有一个header和body,并且header有一个固定的大小(比如128字节),这种情形下可以正常工作。

    Gathering Writes

    "gathering write"把多个buffer的数据写入到同一个channel中,下面是示意图:

    gather.png

    Java NIO: Gathering Write

    用代码表示的话如下:

    ByteBuffer header = ByteBuffer.allocate(128);
    ByteBuffer body   = ByteBuffer.allocate(1024);
    //write data into buffers
    ByteBuffer[] bufferArray = { header, body };
    channel.write(bufferArray);

    类似的传入一个buffer数组给write,内部机会按顺序将数组内的内容写进channel,这里需要注意,写入的时候针对的是buffer中position到limit之间的数据。也就是如果buffer的容量是128字节,但它只包含了58字节数据,那么写入的时候只有58字节会真正写入。因此gathering write是可以适用于可变大小的message的,这和scattering reads不同。

     ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

    感谢******阿贝云******提供的免费云服务器和免费虚拟主机,1C1G5M配置,搭配内网穿透,真香,看视频听歌曲无压力,*

    运行起来也相当流畅,网速个人使用是真的赞,欢迎大家使用

    ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

  • 相关阅读:
    CodeForces 7B
    CodeForces 4D
    离散化
    线段树入门
    洛谷 P3951 小凯的疑惑(赛瓦维斯特定理)
    Codeforces 1295D Same GCDs (欧拉函数)
    Codeforces 1295C Obtain The String (二分)
    Codeforces 1295B Infinite Prefixes
    Codeforces 1295A Display The Number(思维)
    Codeforces 1294F Three Paths on a Tree(树的直径,思维)
  • 原文地址:https://www.cnblogs.com/zqq1234/p/12907644.html
Copyright © 2011-2022 走看看