zoukankan      html  css  js  c++  java
  • java nio

    nio

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式

    标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

    Channel

    • Channel

      • FileChannel
      • DatagramChannel
      • SocketChannel
      • ServerSocketChannel

           FileInputStream fis=new FileInputStream("123");
           FileChannel channel=fis.getChannel();
        

        Channel是与Buffer可以进行双向的读写操作的结构,关于如何读写可以看下面的Buffer

    Buffer

    • Buffer
      • ByteBuffer
      • CharBuffer
      • DoubleBuffer
      • FloatBuffer
      • IntBuffer
      • LongBuffer
      • ShortBuffer
      • MappedByteBuffer

    缓冲区可以进行读写操作,最大大小的开辟是确定的

         IntBuffer ib=IntBuffer.allocate(20);
    

    Buffer里面的三个重要属性

    • capacity 表示buffer的容量,是定值
    • position 表示当前的读写指针
    • limit 表示当前读写的最大的位置,就是说position不能超过limit

    不论读还是写,都是从postion位置开始操作,limit相当于一个postion的阈值,Buffer的主要函数都是通过修改position和limit的值来实现功能的

    1. flip()
      从写状态变为读状态,limit=position,position=0
    2. rewind()
      将position变成0,主要是用于你读数据的时候,可以回过头重新读一遍
    3. clear()
      完全清空,position=0,limit=max,变为写状态,但是实际上原来的数据没有抹掉
    4. compact()
      方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
    5. mark()与reset()方法
      通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position,用来标记一个position位置的作用
    6. equals()与compareTo()方法
      比较的是从 position到limit之间的元素,他们之间的元素才是程序承认的有效元素
    7. hasremaining 还有没有有效数据

    读写方法

    1. 从Channel写到Buffer
      int bytesRead = inChannel.read(buf); //从channel读到buffer
    2. 通过put方法写Buffer的例子:
      buf.put(127);// 这就比较简单了,直接put,有很多的复写的方法,大体类似

    3. 从Buffer读取数据到Channel的
      int bytesWritten = inChannel.write(buf);

    4. 使用get()方法从Buffer中读取数据
      byte aByte = buf.get();

    Scatter/Gather

    Scatter/Gather是针对于Channel的一个特性,可以把Channel数据分发给多个Buffer,或者把多个Buffer合并到一个Channel
    他的价值在于,可以把固定的包头给单独的拿出来放到某一个Buffer中处理,注意在分发时,只有第一个BUffer填满了,才会去填第二个Buffer,特别要主要position和limit的值的变化

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

    通道之间的数据传输

    FileChannel有两个方法transferTo/transferFrom,来完成通道之间数据的传递,参数可以是任意通道。

        long position = 0;
        long count = fromChannel.size();//最大传输的数据量,实际上可能没有这么大,有多少传多少
        fromChannel.transferTo(position, count, toChannel);
    
        long position = 0;
        long count = fromChannel.size();////最大传输的数据量,实际上可能没有这么大,有多少接收多少
        toChannel.transferFrom(position, count, fromChannel);
    

    FileChannel

    可以使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度后面的部分将被删除。如:
    channel.truncate(1024);

    FileChannel实例的size()方法将返回该实例所关联文件的大小。如:
    long fileSize = channel.size();

    FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到FileChannel里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。 force()方法有一个boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。 下面的例子同时将文件数据和元数据强制写到磁盘上:
    channel.force(true);

    Pipe

    Pipe是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取

        Pipe p=Pipe.open();//打开Pipe
        //Pipe入口
        Pipe.SinkChannel sinkChannel=p.sink();
        //Pipe出口
       Pipe.SourceChannel sourceChannel=p.source();
       //入口端写入
        String s="Write to Pipe ......";
        ByteBuffer bf=ByteBuffer.allocate(2000);
        bf.put(s.getBytes());
        bf.flip();
        while (bf.hasRemaining()) {
            sinkChannel.write(bf);
        }
       //出口端写出
        ByteBuffer bf1=ByteBuffer.allocate(2000);
        sourceChannel.read(bf1);
        System.out.println(new String(bf1.array()));
    

    SocketChannel

    SocketChannel socketChannel = SocketChannel.open();
    socketChannel.connect(new InetSocketAddress("IP",port));
    

    读数据

    ByteBuffer buf = ByteBuffer.allocate(48);
    int bytesRead = socketChannel.read(buf);
    

    写数据

        ByteBuffer buf = ByteBuffer.allocate(48);
        buf.clear();
        buf.put(newData.getBytes());
        buf.flip();
        while(buf.hasRemaining()) {
            channel.write(buf);
        }
    

    非阻塞模式
    socketChannel.configureBlocking(false);
    非阻塞模式与选择器搭配会工作的更好,通过将一或多个SocketChannel注册到Selector,可以询问选择器哪个通道已经准备好了读取,写入等

    ServerSocketChannel

    • socket()返回对应的Socket对象
    • accept()接受到此通道套接字的连接。返回的是一个SocketChannel,在分阻塞模式的时候,accept没有连接就会返回null

    Selector

    Selector相当于一个事件管理器,可以向它注册不同连接的多个事件,并以一定间隔询问Selector是否有注册的时间发生,再进行后续的处理。这样只需要一个阻塞的线程,就可以管理所有的连接了。
    Selector注册的Chnannel必须是非阻塞的,因此FileChannel肯定不能用Selector了。主要应用还是Socket。

    Selector管理的事件包含两个要素: 1. 事件所对应的连接,以SocketChannel作为描述。 2. 事件的类型,SelectionKey.OP_* 常量之一。 两个要素唯一确定一个发生的事件。

    主要的逻辑包括

    • 将Channel注册到Selector上,并且说明我这个Channel要监听的事件的类型
    • 调用int n = selector.select(),如果n>0说明有监听的事件发生了
    • 调用selector.selectedKeys()返回一个集合,每一个事件对应一个 SelectionKey 对象。通过 SelectionKey 获取对应的 SocketChannel 以及事件的类型,就能对SocketChannel做你想要做的必须的操作。

    其他的知识点

    事件的类型
    1、SelectionKey.OP_CONNECT
    2、SelectionKey.OP_ACCEPT
    3、SelectionKey.OP_READ
    4、SelectionKey.OP_WRITE

    select()方法会阻塞,直到至少有一个channel的注册事件已就绪。
    select(long timeout)和select()一样,但阻塞时间最大为timeout 毫秒。
    selectNow()不会阻塞,不管有没有channel就绪,都立刻返回。

    示例代码

  • 相关阅读:
    loj6388 「THUPC2018」赛艇 / Citing
    loj6387 「THUPC2018」绿绿与串串 / String
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---适配器模式之TurkeyAdapter[转]
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---策略模式之MiniDuckSimulator[转]
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---装饰模式之StarBuzzCoffee[转]
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---门面模式之HomeTheater[转]
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---命令模式之RemoteControlTest[转]
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---命令模式之SimpleRemoteWithUndoTest[转]
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---命令模式之SimpleRemoteControlTest [转]
    Delphi 设计模式:《HeadFirst设计模式》Delphi7代码---状态模式[转]
  • 原文地址:https://www.cnblogs.com/Coder-Pig/p/6599109.html
Copyright © 2011-2022 走看看