Java NIO

1,324 阅读3分钟

内容来源于博客 jenkov

1. Java NIO Tutorial

Channels and Buffers

在标准的 IO API 中,使用字节流(Byte Stream)和字符流(Character Stream)。在 NIO 中,使用的是 Channel 和 Buffer。数据总是从 Channel 读到 Buffer,或者从 Buffer 写到 Channel 。

Non-blocking IO

Java NIO 可以做 Non-blocking IO。例如,一个线程可以要求一个 Channel 将数据读到一个 Buffer 中。当 Channel 将数据读入 Buffer 时,线程可以做其他事情。一旦数据被读入 Buffer ,线程就可以继续处理数据。向 Channel 写入数据也是如此。

Selectors

Java NIO 包含“选择器”的概念。一个选择器是一个对象,它可以监控多个 Channel 的事件(如:连接打开,数据到达等)。因此,一个线程可以监控多个 Channel 的数据。

2. Java NIO Overview

Java NIO由以下核心组件组成:

  • Channels
  • Buffers
  • Selectors

Java NIO的类和组件不止这些,但 Channel、Buffer 和 Selector 构成了API的核心。其余的组件,如 Pipe 和FileLock 只是与这三个核心组件配合使用的实用类。

Channels and Buffers

通常情况下,NIO 中所有的 IO 都是从一个 Channel 开始的。一个 Channel 有点像一个流。从 Channel 中可以将数据读到 Buffer 中。数据也可以从 Buffer 写入 Channel。

Java NIO: Channels read data into Buffers, and Buffers write data into Channels

有几种Channel和Buffer类型。

Java NIO 中主要的 Channel 实现列表:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

这些通道涵盖了 UDP+TCP 网络 IO,以及文件 IO。

Java NIO 中核心 Buffer 实现列表:

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

这些 Buffer 涵盖了你可以通过 IO 发送的基本数据类型:byte、short、int、long、float、double 和 characters。

Java NIO 还有一个 MappedByteBuffer,它与内存映射文件一起使用。

Selectors

一个 Selector 允许一个线程处理多个 Channel。如果应用程序有许多连接(Channel)打开,但每个连接的流量很小,这就很方便。例如,在一个聊天服务器中。

下面是一个使用选择器来处理3个Channel的线程的例子。

Java NIO: A Thread uses a Selector to handle 3 Channel's

要使用一个 Selector,在它那里注册 Channel,然后调用它的 select() 方法。这个方法会阻塞,直到有为注册的一个 Channel 准备好一个事件。一旦该方法返回,线程就可以处理这些事件。事件的例子有传入连接、接收数据等。

Java NIO Channel

Java NIO 通道与流类似,但有一些不同:

  • 你既可以读也可以写到 Channel。流通常是单向的(读或写)。
  • Channel 可以异步地读和写。
  • Channel 总是读到或写到一个 Buffer。

Channel Implementations

Java NIO 中最重要的 Channel 实现:

  • FileChannel:可以从文件中读取数据,也可以向文件中读取数据。
  • DatagramChannel:可以通过 UDP 在网络上读写数据。
  • SocketChannel:可以通过 TCP 在网络上读写数据。
  • ServerSocketChannel:允许您监听传入的 TCP 连接,就像 Web 服务器一样。对于每个传入的连接,都会创建一个SocketChannel。

Basic Channel Example

一个基本的例子,使用 FileChannel 将一些数据读到一个 Buffer 中:

RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel inChannel = aFile.getChannel();

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);
while (bytesRead != -1) {

    System.out.println("Read " + bytesRead);
    buf.flip();

    while(buf.hasRemaining()){
        System.out.print((char) buf.get());
    }

    buf.clear();
    bytesRead = inChannel.read(buf);
}
aFile.close();

请注意 buf.flip() 的调用。首先你读入一个 Buffer。然后 flip 它,然后你再从它里面读出。