JAVA NIO

中文教程 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: [email protected]
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)