JAVA NIO

作者: xx1994 | 来源:发表于2018-11-29 16:07 被阅读0次

    中文教程 http://ifeve.com/java-nio-all/
    原地址 http://tutorials.jenkov.com/java-nio/index.html

    概述

    核心组件:

    • Channels
    • Buffers
    • Selectors

    Channel 有点像流,数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中,Channel的主要实现:

    • FileChannel
    • DatagramChannel
    • SocketChannel
    • ServerSocketChannel
      这些涵盖了UDP和TCP网络IO,和文件IO

    Buffer 在NIO中的主要实现:

    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer
      覆盖了通过IO发送的基本数据类型:byte、short、int、long、float、double 和 characters

    Selectors 允许单线程处理多个Channel,如果应用有很多连接(Channel),但是每个连接只有很低的流量,使用Selectors就会很方便,如下图一个聊天服务,一个线程使用一个Selector处理3个Channel
    多线程的上下文切换开销很大,每个线程还得占用资源,使用Selector便可以使用少量的线程,处理更多的Channel

    image.png

    要使用Selector,需要向Selector中注册Channel,然后调用select()方法,这个方法会一直阻塞到某个注册的Channel有事件就绪。一旦这个方法返回,线程就就可以处理这些事件。事件的例子有如新连接进来,数据接收等。

    详述

    Channel
    channel和流(stream)的区别:

    • 既可以从Channel中读数据,同时也可以往通道中写数据。流的读写是单向的
    • Channel可以异步的进行读写
    • Channel总是把数据读到Buffer或者从一个Buffer中写入

    Buffer
    缓冲区实际上就是一块可以先写入数据,然后从中读取数据的内存块,这块内存被包装成NIO Buffer对象,并提供一组方法便于访问。

    使用Buffer去读或者写数据一般遵循以下四个步骤:

    1. 写入数据到Buffer
    2. 调用buffer.flip()
    3. 从Buffer中读出数据
    4. 调用buffer.clear()或者buffer.compact()

    当向Buffer写入数据时,Buffer会跟踪写入的数据量,一旦需要读这些数据,需要使用filp()方法将Buffer从写模式切换到读模式,在读的模式下,便可以读取之前写入的所有数据。
    一旦读取完所有数据,需要清空缓存区,让他可以再次被写入(调用clear()或者compact())。clear方法会清空整个缓冲区,compact只会清除已经读过的数据,未读的数据会移到缓冲区起始位置,新写入的数据会放到这些未读数据之后。

    Buffer的三个属性:

    1. capacity :缓冲区内存块的容量
    2. position :根据读或写操作动态改变。写:初始为0,写入数据后,position移到下一个可插入数据的位置;读:将Buffer切换为read模式,position重置为0,读取数据时,position移动到下一个可读位置
    3. limit:写模式下,代表最多能写入多少数据,等同于capacity;读模式下,代表最多能读多少数据,即写模式下的position值。相当于你能读到之前写入的所有数据。
    image.png

    Java NIO 和 IO的区别

    IO NIO
    面向流 面向缓冲
    阻塞IO 非阻塞IO
    Selectors

    面向流与面向缓冲
    面向流意味着每次从流中读取一个或多个字节,直到读取完所有字节,没有被缓存在任何地方。
    面向缓冲便是将数据读取到一个缓冲区,需要时可以在缓冲区中前后移动取数据,增加灵活性

    阻塞与非阻塞IO
    阻塞IO意味着一个线程调用read()或write()时,该线程阻塞,直到数据被读取或者完全写入,此过程期间该线程不能干别的事情。
    非阻塞IO是一个线程通过一个Channel请求读取数据,仅能读取当前可用数据,如果没有可用数据,便不会读取,而不是保持线程阻塞。所以直至数据变成可读取之前,该线程可以继续做别的事情。一个线程管理多个Channel,便可以在空闲时间处理其他Channel上的任务

    Selectors 帮助一个Thread管理多个Channel

    比如读取如下文本

    Name: Anna
    Age: 25
    Email: anna@mailserver.com
    Phone: 1234567890
    

    IO的方法:

    InputStream input = ... ; // get the InputStream from the client socket
    
    BufferedReader reader = new BufferedReader(new InputStreamReader(input));
    
    String nameLine   = reader.readLine();
    String ageLine    = reader.readLine();
    String emailLine  = reader.readLine();
    String phoneLine  = reader.readLine();
    

    每次使用reader.readLine()得到返回值时,便知道该行已经读取完毕

    使用NIO

    ByteBuffer buffer = ByteBuffer.allocate(48);
    
    int bytesRead = inChannel.read(buffer);
    
    while(! bufferFull(bytesRead) ) {
        bytesRead = inChannel.read(buffer);
    }
    

    每次isChannel.read()返回时,缓存区内存在哪些数据并不清楚,需要bufferFull()判断缓冲区是否准备好被处理。

    所以使用NIO对于读取数据解析过程会比阻塞IO读取数据更复杂

    相关文章

      网友评论

          本文标题:JAVA NIO

          本文链接:https://www.haomeiwen.com/subject/gbefcqtx.html