Java NIO 要点总结

来自Jenkov.com的比较完整但是足够brief的一个系列:Java NIO Tutorial,介绍了NIO的主要机制和其中几个重要对象的作用和工作。

1. 三个对象

NIO核心的三个对象:

  • Channels
  • Buffers
  • Selectors

简单讲三个对象:Channel 像IO的流,Buffer就像名字一样,就是个缓存。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。IO是面向流的,连接到一个源或者目标(对应于输入流或者输出流),如Java IO Overview中说明,比较典型的数据源和目标类型有:Files、Pipes、Network Connections、In-memory Buffers (e.g. arrays)、System.in, System.out, System.error。而Chanel也是联通类似的源或者目标,但是读写都是要经过Buffer。以上比较好理解,参照Java NIO ChannelJava NIO Buffer对应部分说明即可。但是第三个对象Selector则是NIO独有的概念,重点说明下。

2. 选择器Selector

参照Java NIO Selector中的一张图

overview-selectors

在一个单线程中使用一个Selector处理3个Chann。这恐怕是和标准IO最大的差别,想在IO中我们要同时处理三个连接上的传输,最直接的办法就是创建三个线程分别处理。而在NIO中,使用selector,只需把三个Channel注册到Selector上,调用select()方法(当然Selector还提供了非阻塞的select()方法,见下文),则会一直阻塞到注册时间就像,然后线程就可以处理了。

一个比较典型的代码片段

例子中?Selectionkey.OP_READ是channel注册到Selector时,告诉Selector监听这个channel上的.OP_READ类型的事件。A channel that "fires an event" is also said to be "ready" for that event.?

Selector支持四中类型事件,定义在Selectionkey中的四个常量,表示如下”ready“的意思。

  • Connect :channel成功连接到另一个服务器称为“连接就绪”
  • Connect :一个server socket channel准备好接收新进入的连接称为“接收就绪”
  • Read:一个有数据可读的通道可以说是“读就绪”
  • Write:一个通道可以写数据了,即”写就绪”

根据常量定义也可以猜到,如果需要在一个通道上同时注册多个事件时,可以”位或“操作下即可。

注意select方法有三种形式,在J.U.C的AQS中非常常见。

  • select()?阻塞到至少有一个通道在你注册的事件上就绪了。This method performs a blocking selection operation. It returns only after at least one channel is selected, this selector’s wakeup method is invoked, or the current thread is interrupted, whichever comes first.
  • select(long timeout) 功能同select(),最长会阻塞timeout毫秒
  • selectNow()不会阻塞,立即返回。This method performs a non-blocking selection operation. If no channels have become selectable since the previous selection operation then this method immediately returns zero.

3.?NIO和IO的比较

机制上差别

主要差别如下表

IO NIO
Stream oriented Buffer oriented
Blocking IO Non blocking IO
Selectors

IO是面向流,NIO是面向缓存的。前面开头讲过了,Java IO流就像生活中的管道,顺序的一个一个往前读,不能前后移动流中的数据(覆水难收不是想说这个意思,但是表达了一点这个意思)。NIO则督导一点数据缓存到Buffer,使用时候可以前后移动,操作比较灵活,见Buffer中position、limit等几种重要概念的说明。

最主要的特种是Java IO中的各种流是阻塞的,即调用read或者write方法时候线程会阻塞直到读完或者写完。NIO是费阻塞模式,线程发送读数据请求后,如果当前没有数据可读则不读取,线程继续做其他事情。当然这种基础是selector模型的支持。

即一个单独的线程来监视多个channel,多个channel注册到一个Selector上,用一个单独的线程来监控并“选择”通道,哪些可以读了,哪些些可以写了。

使用上差别

从机制上差别不难看出,如果同时一个服务端要相应很多连接,每个连接只有很少的数据,则使用NIO比较合适,即一个线程对应对个连接。很多web服务器,像Jetty、Tomacat里面就是使用了NIO,可以比较轻量的处理很多连接。

nio-vs-io-3

但如果要维持对个连接,每个连接上一直有很多数据在发生,非常高的带宽,一次发送数据量比较大,则使用单独线程来维护一个连接比较适合。

nio-vs-io-4

 

4. Buffer中的三个属性

说明Buffer中capacity、position和limit三个重要属性的作用。

  • capacity:内存块的固定大小,写满了必须清空才能接着写。
  • position:表示操作的当前位置。写数据时,初始化为0,写一个则向前移动一个位置,最大capacity-1; 读数据时候,从position位置开始读,读一个向前移动一个位置。
  • limit:写模式下表示最多还能向Buffer中写多少;读模式下,limit就是capacity取值。

buffers-modes

Buffer的一个重要方法flip(),表示Buffer从写状态切换到读状态。实际操作见代码中,即把limit设置成当前位置,即写操作写的位置,position设置为0,表示从头读,mark标记清除掉。

补充说明下。Buffer的写操作可以是如下两种方式,一种是给buffer中写入数据,另一钟则是从channel中读取数据写到Buffer中。

而Buffer的读操作对应的也可以是调用Buffer的get方法(abstract class Buffer类中没有规定get、put这样的方法,但是在其众多子类中又类似的方法)或者Buffer中读取写到channel。

 

5. 附录一:NIO 来自Java NIO Tutorial

1

Java NIO Tutorial

2

Java NIO Overview

3

Java NIO Channel

4

Java NIO Buffer

5

Java NIO Scatter / Gather

6

Java NIO Channel to Channel Transfers

7

Java NIO Selector

8

Java NIO FileChannel

9

Java NIO SocketChannel

10

Java NIO ServerSocketChannel

11

Java NIO DatagramChannel

12

Java NIO Pipe

13

Java NIO vs. IO

14

Java NIO Path

15

Java NIO Files

原创文章。为了维护文章的版本一致、最新、可追溯,转载请注明: 转载自idouba

本文链接地址: Java NIO 要点总结


No comments yet.

发表评论