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配置,搭配内网穿透,真香,看视频听歌曲无压力,*

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

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

  • 相关阅读:
    hdu 4947
    hdu 4946
    hdu 4944
    hdu 4942
    hdu 4941
    PAT 【L2-011 玩转二叉树】
    PAT【L2-006 树的遍历】
    XYNUOJ 【2070: 重建二叉树】
    XYNUOJ 【1367: 二叉链表存储的二叉树】
    XYNUOJ 2390【二叉树遍历2】
  • 原文地址:https://www.cnblogs.com/zqq1234/p/12907644.html
Copyright © 2011-2022 走看看