Java NIO分析

  • 原有的 IO 是面向流的、阻塞的,NIO 则是面向块的、非阻塞的

  • NIO在数据的读取和写入都不是阻塞的

    • 从通道读取数据时候可能读到数据也可能读不到数据,读不到数据也不会阻塞
    • 写入数据时也不需要阻塞到数据完全写入,只需要将数据交给缓存即可
  • NIO因为线程不阻塞,所以一个线程可以处理多个IO读写事件,阻塞IO的话一个IO请求就要分配一个线程来处理

  • 在标准IO API中,你可以操作字节流和字符流,但在新IO中,你可以操作通道和缓冲,数据总是从通道被读取到缓冲中或者从缓冲写入到通道。

  • Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,再将事件分发出去给其他线程处理

  • java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:

    • 服务端接收客户端连接事件 | SelectionKey.OP_ACCEPT(16)
    • 客户端连接服务端事件 | SelectionKey.OP_CONNECT(8)
    • 读事件 | SelectionKey.OP_READ(1)
    • 写事件 | SelectionKey.OP_WRITE(4)
  • java NIO使用了Selector的机制,可以将自己对某个通道感兴趣的事件注册在Selector上,Selector基于内部的机制,会在产生事件的时候进行通知,我们就可以拿到对应的事件进行处理,Selector有两个重要的实现

    • PollSelectorImpl

      这个实现使用了poll的方式实现多路复用选择器,poll会使用轮询的方式检查Sokcet对应事件是否就绪,内部使用链表的方式存储(不确定,源码没找到链表)。

    • EPollSelectorImpl

      这个实现使用了epoll的方式实现多路复用选择器,epoll可以监控的文件描述符数量是可以打开文件的数量上限,epoll获取事件不是通过轮询得到,而是通过给每个文件描述符定义回调得到,linux会在对应的事件就绪时候进行回调通知,该实现也是linux的特有实现。

  • 通过调用selector.select()来得到已经就绪的事件,并对事件进行处理

    • select方法内部会进行阻塞,直到有事件返回
    • 也可以使用带有超市时间的select方法,会在超时后直接返回
    • 返回的是一个可以处理的事件数量
  • 通过selector.selectedKeys()获取可以处理的事件集合

    • 代表一个个可以处理的事件SelectionKey
    • 每个SelectionKey只包含一个事件,同时内部持有对应的通道(Channel),以下四个方法代表上面对应的四个事件的检查,用以检查是否是对应的事件完成
      • isAcceptable
      • isConnectable
      • isWritable
      • isReadable
    • 从对应的事件中得到通道,可以对通道进行处理,比如接受连接,写入消息,读取消息等等
    • 处理过的SelectionKey需要从集合移除,不然会重复处理
  • ByteBuffer

    • NIO针对数据的操作都是针对缓存的,和以前针对流不同
    • 从通道读取数据需要先将数据读取到缓冲区,然后再操作缓冲区
    • 写入数据到通道也需要先将数据写入到缓冲区,然后再操作通道将缓冲区写入到通道
    • nio数据的粘包也需要再这里处理,有些包也许一个缓冲区装不下,就需要利用多个缓冲区将包黏在一起,最后形成一个真正的数据包
    • 使用ByteBuffer.allocate可以分配一个缓冲空间,缓冲空间有堆空间和直接空间之分,堆空间就是在堆上分配的一个缓冲空间,直接空间就是在外部直接内存上分配的内存空间。
    • ByteBuffer内部使用一些下标的标志位来维护数据

你可能感兴趣的:(Java NIO分析)