内容来源于博客 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。
有几种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的线程的例子。
要使用一个 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 它,然后你再从它里面读出。